├── .gitignore ├── local.sh ├── repl.sh ├── test.sh ├── watch_test.sh ├── elf-lang-logo.png ├── elf-lang-logo-small.png ├── Dockerfile ├── elf_names.h ├── .github └── workflows │ ├── test.yml │ └── docker.yml ├── elf_ref.pl ├── elf_map.pl ├── examples ├── aoc2023_day1.elf ├── elves.elf └── aoc2023_day1.txt ├── LICENSE ├── elf_file.pl ├── test.h ├── elf_scanner.h ├── elf_record.pl ├── elf_dev.pl ├── elf_names.c ├── elf_fmt.pl ├── elf.h ├── examples.pl ├── README.md ├── index.html ├── methods.html ├── elf_scanner.c └── elf.pl /.gitignore: -------------------------------------------------------------------------------- 1 | /elf 2 | /where_exe 3 | -------------------------------------------------------------------------------- /local.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | python3 -m http.server 8888 3 | -------------------------------------------------------------------------------- /repl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd "$(dirname "$0")" 3 | swipl -q -t repl elf.pl 4 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | clang -DTEST=1 -o test $1.c 4 | time ./test 5 | -------------------------------------------------------------------------------- /watch_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | fswatch -o $1.c | xargs -n1 -I{} ./test.sh $1 4 | -------------------------------------------------------------------------------- /elf-lang-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tatut/elf-lang/main/elf-lang-logo.png -------------------------------------------------------------------------------- /elf-lang-logo-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tatut/elf-lang/main/elf-lang-logo-small.png -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM swipl:9.2.8 2 | RUN mkdir /app 3 | COPY elf*.pl /app/ 4 | COPY examples.pl /app/ 5 | COPY repl.sh /app/ 6 | CMD ["sh", "/app/repl.sh"] -------------------------------------------------------------------------------- /elf_names.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef struct ElfName { 4 | uint32_t hash; 5 | char* name; 6 | } ElfName; 7 | 8 | /** 9 | * Intern the given name. Always returns the same shared 10 | * ElfName for the same string name. 11 | * Interned names are never freed. 12 | */ 13 | const ElfName *intern(char *name); 14 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: [push] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: install swipl 8 | run: | 9 | sudo apt-add-repository ppa:swi-prolog/stable 10 | sudo apt-get update 11 | sudo apt-get install swi-prolog 12 | - uses: actions/checkout@v2 13 | - name: run tests 14 | run: swipl -g "run_tests(elf)." -t halt elf.pl -------------------------------------------------------------------------------- /elf_ref.pl: -------------------------------------------------------------------------------- 1 | :- module(elf_ref, [ref_new/2, ref_set/2, ref_get/2, ref_cleanup/0]). 2 | :- use_module(library(gensym)). 3 | :- dynamic ref/2. 4 | 5 | ref_new(ref(ID), Initial) :- gensym(ref, ID), 6 | asserta(ref(ID, Initial)). 7 | 8 | ref_get(ref(ID), Val) :- ref(ID, Val). 9 | ref_set(ref(ID), NewVal) :- 10 | retractall(ref(ID, _)), 11 | asserta(ref(ID, NewVal)). 12 | 13 | ref_cleanup :- retractall(ref(_,_)). 14 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Docker image for ghcr.io 2 | on: [push] 3 | jobs: 4 | build_and_publish: 5 | runs-on: ubuntu-latest 6 | environment: elf-lang 7 | steps: 8 | - uses: actions/checkout@v3 9 | - name: Build and push the image 10 | run : | 11 | docker login --username tatut --password ${{ secrets.GH_PAT }} ghcr.io 12 | docker build . --tag ghcr.io/tatut/elf-lang:latest 13 | docker push ghcr.io/tatut/elf-lang:latest 14 | -------------------------------------------------------------------------------- /elf_map.pl: -------------------------------------------------------------------------------- 1 | :- module(elf_map, [map_new/1, map_put/4, map_get/3, map_pairs/2, 2 | map_keys/2, map_del/3, 3 | map_size/2]). 4 | :- use_module(library(rbtrees)). 5 | :- use_module(library(yall)). 6 | 7 | map_new(map(M)) :- rb_new(M). 8 | 9 | map_put(map(M0), Key, Val, map(M1)) :- rb_insert(M0, Key, Val, M1), !. 10 | 11 | map_get(map(M), Key, Val) :- rb_lookup(Key, Val, M) -> true; Val=nil. 12 | 13 | map_del(map(M0), Keys, map(M1)) :- 14 | foldl([K,MIn,MOut]>>rb_delete(MIn,K,MOut), Keys, M0, M1). 15 | 16 | map_pairs(map(M), Pairs) :- 17 | ground(Pairs), 18 | list_to_rbtree(Pairs, M), !. 19 | 20 | map_pairs(map(M), Pairs) :- 21 | findall(K-V, rb_in(K,V,M), Pairs0), 22 | sort(Pairs0, Pairs). 23 | map_keys(map(M), Keys) :- 24 | findall(K, rb_in(K,_V,M), Keys). 25 | 26 | map_size(map(M), Size) :- rb_size(M, Size). 27 | -------------------------------------------------------------------------------- /examples/aoc2023_day1.elf: -------------------------------------------------------------------------------- 1 | # Solution for Advent of Code 2023, day 1 2 | # https://adventofcode.com/2023/day/1 3 | # 4 | # Goes through lines, calculates calibration value for each line, and sums the values 5 | 6 | # Read input file to a list of lines 7 | input: "examples/aoc2023_day1.txt" lines, 8 | 9 | # Generic solution, sum calling calibration value on each line 10 | solve: {cal| input sum({cal call($)})}, 11 | 12 | # Part1, just keep digits 13 | part1: solve call({$ keep(&digit), (10 * _ first) + _ last}), 14 | "Part1: %d" fmt(part1) print, 15 | 16 | # Part2 requires spelled variants as well 17 | spelled: [[0, "zero"], [1, "one"], [2, "two"], [3, "three"], [4, "four"], 18 | [5, "five"], [6, "six"], [7, "seven"], [8, "eight"], [9, "nine"]], 19 | part2: solve call({line| line heads keep({h| spelled some({h starts?($ last) if($ first, h first digit) })}), (10 * _ first) + _ last}), 20 | "Part2: %d" fmt(part2) print, 21 | 22 | # Return both results 23 | [part1, part2] 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Tatu Tarvainen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /elf_file.pl: -------------------------------------------------------------------------------- 1 | :- module(elf_file, [file_codes/2, file_lines/2]). 2 | 3 | % Set to true when using from browser 4 | :- set_prolog_flag(elf_use_fetch, false). 5 | 6 | file_codes(FileName, Codes) :- 7 | prolog_flag(elf_use_fetch, true), !, 8 | string_codes(FileStr, FileName), 9 | fetch(FileStr, text, Str), 10 | string_codes(Str, Codes), 11 | _ := console.log(Codes). 12 | 13 | file_codes(FileName, Codes) :- 14 | prolog_flag(elf_use_fetch, false), !, 15 | atom_codes(F, FileName), 16 | read_file_to_codes(F, Codes, []). 17 | 18 | file_string(FileName, String) :- 19 | prolog_flag(elf_use_fetch, true), !, 20 | string_codes(FileStr, FileName), 21 | fetch(FileStr, text, String). 22 | 23 | file_string(FileName, String) :- 24 | prolog_flag(elf_use_fetch, false), !, 25 | string_codes(FileStr, FileName), 26 | read_file_to_string(FileStr, String, []). 27 | 28 | file_lines(FileName, LinesCs) :- 29 | file_string(FileName, String), 30 | split_string(String, "\n", "", LinesIn), 31 | ( last(LinesIn, "") 32 | -> append(Lines, [""], LinesIn) 33 | ; Lines = LinesIn), 34 | maplist(string_codes, Lines, LinesCs). 35 | -------------------------------------------------------------------------------- /examples/elves.elf: -------------------------------------------------------------------------------- 1 | Elf{name, age}, 2 | 3 | elves: [Elf{name: "Jingleberry Twinkletoes", age: 243}, 4 | Elf{name: "Merry Mistletoe", age: 182}, 5 | Elf{name: "Sugarplum Sparklesocks", age: 156}, 6 | Elf{name: "Holly Jollyjingle", age: 317}, 7 | Elf{name: "Biscuit Peppermint", age: 75}, 8 | Elf{name: "Tinsel Fuzzyfoot", age: 206}, 9 | Elf{name: "Ginger Snapapple", age: 138}, 10 | Elf{name: "Twinkle Toffeecheeks", age: 293}, 11 | Elf{name: "Frosty Gigglesocks", age: 211}, 12 | Elf{name: "Nibbles Snowcrisp", age: 92}, 13 | Elf{name: "Buttons Marshmallow", age: 176}, 14 | Elf{name: "Taffy Sugarplop", age: 89}, 15 | Elf{name: "Snickerdoodle Twinkles", age: 249}, 16 | Elf{name: "Sparkleberry Shimmer", age: 198}, 17 | Elf{name: "Waffle Butterbun", age: 112}, 18 | Elf{name: "Merry Crinkle", age: 311}, 19 | Elf{name: "Glitter Gumdrop", age: 143}, 20 | Elf{name: "Tootsie Snowflake", age: 166}, 21 | Elf{name: "Cherry Frostnibble", age: 97}, 22 | Elf{name: "Sprinkle Jinglepop", age: 225}], 23 | 24 | Elf.greet: { "Hi %s! My name is %s and I'm %d years old!" fmt($, my name, my age) } 25 | -------------------------------------------------------------------------------- /test.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | char *current_test; 5 | 6 | size_t test_success=0; 7 | size_t test_failure=0; 8 | 9 | #define testing(name, body) \ 10 | printf("Testing: %s\n", name); \ 11 | body 12 | 13 | #define it(name, body) \ 14 | printf(" %s", name); \ 15 | { body } \ 16 | printf("\n"); 17 | 18 | #define assert(body) \ 19 | if ((body)) { \ 20 | printf(" ✅"); \ 21 | test_success++; \ 22 | } else { \ 23 | printf(" ❌"); \ 24 | test_failure++; \ 25 | } 26 | 27 | 28 | // must be declared by file 29 | void test(); 30 | 31 | void run_tests() { 32 | test(); 33 | printf("\nSuccess: %ld\nFail: %ld\n", test_success, test_failure); 34 | } 35 | 36 | int main(int argc, char **argv){ 37 | run_tests(); 38 | if(test_failure==0) 39 | return 0; 40 | else 41 | return 1; 42 | } 43 | -------------------------------------------------------------------------------- /elf_scanner.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Header for Elf scanner. 3 | * Defines types and public function signatures. 4 | */ 5 | #include "elf.h" 6 | 7 | typedef enum TokenType { 8 | LITERAL, 9 | IDENTIFIER, 10 | NAMEREF, 11 | MULTIPLY, DIVIDE, ADD, SUBTRACT, MODULO, CONCAT, 12 | COMMA, 13 | PAREN_OPEN, PAREN_CLOSE, // '(' ')' 14 | BRACKET_OPEN, BRACKET_CLOSE, // '[' ']' 15 | BRACE_OPEN, BRACE_CLOSE, // '{' '}' 16 | BAR, // '|' vertical bar 17 | DICT_OPEN, // "%{" special syntax for dictionary 18 | ASSIGN, // ':' 19 | LESSER_THAN, LESSER_THAN_EQUAL, // '<' "<=" 20 | GREATER_THAN, GREATER_THAN_EQUAL, // '>' ">=" 21 | EQUAL, // '=' 22 | EOS, 23 | ERROR 24 | } TokenType; 25 | 26 | typedef union TokenData { 27 | ElfVal val; 28 | char ch; 29 | char *name; 30 | } TokenData; 31 | 32 | typedef struct Token { 33 | TokenType type; 34 | size_t line, column; 35 | TokenData data; // data associated with token 36 | } Token; 37 | 38 | typedef struct Scanner { 39 | char *source, *at; 40 | size_t line, column; 41 | } Scanner; 42 | 43 | Scanner* scanner_new(char* input); 44 | void scanner_free(Scanner *s); 45 | 46 | /** 47 | * Scan for next token. Mutates scanner state as appropriate. 48 | * Returns Token of type EOS if at end of input. 49 | * Returns Token of type ERROR if encountering unrecognized input. 50 | * Tokens may have claimed memory, you must call token_free once done with it. 51 | */ 52 | Token scan(Scanner *s); 53 | 54 | /** 55 | * Free any memory used by the token. 56 | */ 57 | void token_free(Token t); 58 | -------------------------------------------------------------------------------- /elf_record.pl: -------------------------------------------------------------------------------- 1 | :- module(elf_record, [record_field/3, record_method/3, record_data/3, 2 | record_new/2, record_get/3, record_set/4, 3 | get_record_method/5, record_cleanup/0, 4 | record_dict/2]). 5 | :- use_module(library(gensym)). 6 | 7 | % Record definition: record_field(RecordName, Num, FieldName). 8 | % Records are stored as compound terms and mutated with nb_arg. 9 | % Fields also act as 0 arg methods that return the value 10 | :- dynamic record_field/3. 11 | 12 | % Record method definition: record_method(MethodName, ClassOrRecord, fun(...)) 13 | :- dynamic record_method/3. 14 | 15 | % Record fields are stored in record_data(RecordInstanceId, Field, Val) 16 | :- dynamic record_data/3. 17 | 18 | record_new(Record, rec(Record,R)) :- 19 | aggregate_all(max(N), record_field(Record, N, _), Len), 20 | length(Fields, Len), 21 | maplist('='(nil), Fields), 22 | compound_name_arguments(R, data, Fields). 23 | 24 | record_get(rec(Record,R), Field, Val) :- 25 | record_field(Record, N, Field), 26 | arg(N, R, Val). 27 | record_set(rec(Record,R0), Field, Val, rec(Record,R1)) :- 28 | duplicate_term(R0, R1), 29 | record_field(Record, N, Field), 30 | nb_setarg(N, R1, Val). 31 | 32 | get_record_method(Record, Name, Fun, Args0, Args1) :- 33 | % Don't try methods that are field accessors 34 | \+ record_field(Record, _, Name), 35 | once(get_record_method_(Record, Name, Fun, Args0, Args1)). 36 | 37 | get_record_method_(Record, Name, Fun, Args, Args) :- 38 | record_method(Name, Record, Fun). 39 | 40 | get_record_method_(Record, Name, Fun, Args, [NameStr, Args]) :- 41 | record_method(dynamic, Record, Fun), 42 | atom_codes(Name, NameStr). 43 | 44 | % Clear any stored record defs and data when program ends 45 | record_cleanup :- 46 | retractall(record_field(_,_,_)), 47 | retractall(record_method(_,_,_)). 48 | 49 | record_dict(rec(Rec,D), Dict) :- 50 | findall(Field-Val, (record_field(Rec, N, Field), arg(N, D, Val)), Pairs), 51 | dict_pairs(Dict, Rec, Pairs). 52 | -------------------------------------------------------------------------------- /elf_dev.pl: -------------------------------------------------------------------------------- 1 | % Elf dev utilities, for Ace editor to get completions and such 2 | :- module(elf_dev, []). 3 | 4 | method_completion(PrefixStr, Completion, Doc) :- 5 | atom_string(Prefix, PrefixStr), 6 | method_completion_(Prefix, Completion, Doc). 7 | 8 | method_completion_(Prefix, Completion, DocStr) :- 9 | elf:method(Completion/_, Doc), 10 | atom(Completion), 11 | atom_prefix(Completion, Prefix), 12 | string_codes(DocStr, Doc). 13 | 14 | % A defined record field 15 | % Code must be executed before this works and each execution cleans these up 16 | method_completion_(Prefix, Completion, Doc) :- 17 | elf_record:record_field(_, _, Completion), 18 | atom_prefix(Completion, Prefix), 19 | format(string(Doc), 20 | '~w\nReturns value of field ~w\n\n~w(Val)\nReturns new instance of recipient with field ~w set to Val.', 21 | [Completion,Completion,Completion,Completion]). 22 | 23 | % A defined method 24 | method_completion_(Prefix, Completion, Doc) :- 25 | elf_record:record_method(_, Completion, _), 26 | atom_prefix(Completion, Prefix), 27 | format(string(Doc), 28 | '~w\n User defined method ¯\\_(ツ)_/¯', [Completion]). 29 | 30 | methods_start(Out) :- 31 | write(Out, '\nElf method reference

Elf methods

'). 32 | 33 | methods_end(Out) :- 34 | write(Out, '
NameDoc
'). 35 | 36 | method_row(Out, Args) :- 37 | format(Out, '~s~s', Args). 38 | 39 | gen_methods :- 40 | setup_call_cleanup( 41 | open('methods.html', write, Out), 42 | (methods_start(Out), 43 | findall([Method, Doc], elf:method(Method/_, Doc), Methods), 44 | sort(Methods, MethodsSorted), 45 | maplist(method_row(Out), MethodsSorted), 46 | methods_end(Out)), 47 | close(Out)). 48 | -------------------------------------------------------------------------------- /elf_names.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Elf name internment. 3 | * All names of methods and types are interned by the scanner. 4 | * We hold a hashtable here. 5 | * 6 | * A ElfName struct holds the precalculated hash and the pointer to the string data. 7 | * Callers of intern can never own the returned name and must not modify it. 8 | * 9 | */ 10 | #include 11 | #include 12 | #include 13 | #include "elf_names.h" 14 | 15 | // hash from https://stackoverflow.com/a/69812981 16 | #define SEED 0x12345678 17 | uint32_t hash(char* str) { // MurmurOAAT_32 18 | uint32_t h = SEED; 19 | for (; *str; ++str) { 20 | h ^= *str; 21 | h *= 0x5bd1e995; 22 | h ^= h >> 15; 23 | } 24 | return h; 25 | } 26 | 27 | // linked entry for when names collide in buckets 28 | typedef struct ElfNameEntry { 29 | ElfName *name; 30 | struct ElfNameEntry *next; 31 | } ElfNameEntry; 32 | 33 | // PENDING: ideally rehash when adding names, now just statically allocated buckets 34 | #define NBUCKETS 2048 35 | 36 | ElfNameEntry **elf_name_buckets = NULL; 37 | 38 | ElfNameEntry *new_name_entry(char *name, uint32_t h) { 39 | size_t len = strlen(name); 40 | ElfNameEntry *e = malloc(sizeof(ElfNameEntry)); 41 | e->name = malloc(sizeof(ElfName)); 42 | e->name->hash = h; 43 | e->name->name = malloc(sizeof(char)*(len+1)); 44 | strcpy(e->name->name, name); 45 | e->next = NULL; 46 | return e; 47 | } 48 | 49 | const ElfName *intern(char* name) { 50 | if(elf_name_buckets == NULL) { 51 | elf_name_buckets = calloc(sizeof(ElfNameEntry), NBUCKETS); 52 | } 53 | const uint32_t h = hash(name); 54 | size_t b = h % NBUCKETS; 55 | ElfNameEntry *first = elf_name_buckets[b]; 56 | if(first == NULL) { 57 | // first time encountering this, whole bucket empty 58 | elf_name_buckets[b] = new_name_entry(name, h); 59 | return elf_name_buckets[b]->name; 60 | } else { 61 | // chase down the collided names and find the given 62 | ElfNameEntry *at = first, *prev; 63 | while(at != NULL) { 64 | // if this is our name, return it 65 | if(at->name->hash == h && strcmp(at->name->name, name)==0) { 66 | return at->name; 67 | } 68 | prev = at; 69 | at = at->next; 70 | } 71 | // not found, add new entry to prev 72 | prev->next = new_name_entry(name, h); 73 | return prev->next->name; 74 | } 75 | } 76 | 77 | #ifdef TEST 78 | 79 | #include "test.h" 80 | 81 | void test() { 82 | 83 | testing("Existing name", { 84 | it("returns the same name for same input", { 85 | assert(intern("foo") == intern("foo")); 86 | }); 87 | 88 | it("returns different name for different inputs", { 89 | assert(intern("foo") != intern("bar")); 90 | }); 91 | 92 | }); 93 | 94 | } 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /elf_fmt.pl: -------------------------------------------------------------------------------- 1 | % Formatted output 2 | :- module(elf_fmt, [fmt/3, pretty/1, prettyln/1, output/1, outputln/1, join/3]). 3 | :- use_module(elf_map). 4 | :- use_module(elf_record). 5 | :- use_module(library(yall)). 6 | 7 | fmt(PatternCs, Args, Out) :- 8 | string_codes(Pattern, PatternCs), 9 | re_split('%(s|d|w|%)', Pattern, Splits), 10 | with_output_to_codes(fmt_(Splits,Args), Out). 11 | 12 | spec("%s"). 13 | spec("%d"). 14 | spec("%w"). 15 | spec("%%"). 16 | 17 | fmt_([],[]). 18 | fmt_([],[_]) :- throw(extra_arguments_to_fmt). 19 | fmt_(["%%"|Patterns], Args) :- 20 | write('%'), 21 | fmt_(Patterns, Args). 22 | fmt_(["%s"|Patterns], [StrCs|Args]) :- 23 | string_codes(Str, StrCs), 24 | write(Str), 25 | fmt_(Patterns, Args). 26 | fmt_(["%d"|Patterns], [Num|Args]) :- 27 | write(Num), 28 | fmt_(Patterns,Args). 29 | fmt_(["%w"|Patterns], [Thing|Args]) :- 30 | write(Thing), 31 | fmt_(Patterns,Args). 32 | fmt_([Str|Patterns], Args) :- 33 | \+ spec(Str), 34 | write(Str), 35 | fmt_(Patterns, Args). 36 | fmt_([Spec|_], []) :- 37 | spec(Spec), 38 | throw(too_few_arguments_to_fmt). 39 | 40 | printable(N) :- integer(N), between(32, 126, N). 41 | 42 | output(X) :- is_list(X), maplist(printable, X), writef('%s',[X]), !. 43 | output(nil) :- !. % nil prints nothing (unless pretty printing) 44 | output(X) :- writef('%w', [X]). 45 | 46 | outputln(X) :- output(X), nl. 47 | 48 | pretty(_,X) :- is_list(X), maplist(printable, X), string_codes(Str, X), 49 | format('"~s"', [Str]), !. 50 | pretty(_,[]) :- !, format('[]'). 51 | pretty(L,X) :- is_list(X), !, append(Items, [LastItem], X), 52 | succ(L, L1), 53 | format('['), maplist({L1}/[I]>>(pretty(L1,I), format(', ')), Items), 54 | pretty(L1,LastItem), format(']'), !. 55 | pretty(0, nil) :- !. % output nothing on nil (toplevel) 56 | pretty(_, nil) :- !, write('nil'). 57 | pretty(L, map(ID)) :- !, format('%{'), 58 | succ(L, L1), 59 | map_pairs(map(ID), AllPairs), 60 | append(Pairs, [LastKey-LastVal], AllPairs), 61 | maplist({L1}/[K-V]>>(pretty(L1,K), format(': '), pretty(L1,V), format(',\n ')), Pairs), 62 | pretty(L1,LastKey), format(': '), pretty(L1,LastVal), format('}'). 63 | pretty(L, rec(Record,D)) :- 64 | !, succ(L,L1), 65 | format('~w{', [Record]), 66 | pad(L, Record, Pad), 67 | findall(Field-Value, (record_field(Record, _, Field), 68 | record_get(rec(Record,D), Field, Value)), 69 | FieldVals), 70 | once(append(FVs,[LastField-LastVal], FieldVals)), 71 | maplist({L1,Pad}/[F-V]>>(format('~w: ', [F]), pretty(L1,V), format('~s', [Pad])), FVs), 72 | format('~w: ',[LastField]), pretty(L1,LastVal), format('}'). 73 | 74 | 75 | pretty(_L, X) :- write(X). 76 | 77 | pretty(X) :- pretty(0, X). 78 | 79 | prettyln(X) :- pretty(X), nl. 80 | 81 | 82 | pad(0, Atom, [44,10|Pad]) :- 83 | atom_length(Atom, L), 84 | L1 is L + 1, 85 | length(Pad, L1), 86 | maplist(=(32), Pad), !. 87 | pad(_L, _, `, `) :- !. 88 | 89 | join(_, [], []) :- !. 90 | join(_, [X], X) :- !. 91 | join(Sep, [X|Rest], Result) :- 92 | join(Sep, Rest, Res0), 93 | append([X,Sep,Res0], Result). 94 | -------------------------------------------------------------------------------- /elf.h: -------------------------------------------------------------------------------- 1 | #ifndef ELF_H 2 | #define ELF_H 3 | 4 | #include <_types/_uint16_t.h> 5 | #include <_types/_uint8_t.h> 6 | #include 7 | 8 | /* Elf values are always of ElfVal struct. 9 | The type tells what the type of the value is 10 | 4096 first values are reserved for implementation, 11 | rest are available for user defined types. 12 | 13 | Values that can fit in 64bits, are directly stored in the 14 | data. Otherwise it points to another struct. 15 | */ 16 | 17 | typedef union ElfData { 18 | long longVal; 19 | double doubleVal; 20 | char bytesVal[8]; 21 | void *ptrVal; 22 | } ElfData; 23 | 24 | typedef struct ElfVal { 25 | uint16_t type; // 64k types max 26 | ElfData data; 27 | } ElfVal; 28 | 29 | // sizeof(ElfVal) == 16 bytes 30 | 31 | typedef struct ElfCons { 32 | ElfVal value; 33 | struct ElfCons *next; 34 | } ElfCons; 35 | 36 | typedef struct ElfArray { 37 | size_t size[4]; // max size of each dimension, upto 4 dimensions, defaults to 1 38 | ElfVal* data; // 39 | } ElfArray; 40 | 41 | // bytestring of >8 bytes 42 | typedef struct ElfBytes { 43 | size_t length; 44 | char* data; 45 | } ElfBytes; 46 | 47 | #define TYPE_NIL 0 // nil, *data = NULL 48 | #define TYPE_TRUE 1 // true boolean value 49 | #define TYPE_FALSE 2 // false boolean value 50 | #define TYPE_INT 3 // signed 64bit integer, immediate 51 | #define TYPE_DOUBLE 4 // signed 64bit float, immediate 52 | #define TYPE_FLAGS 5 // 64 booleans in a single bitmask, immediate 53 | #define TYPE_LIST 6 // a cons cell, *data is ElfCons 54 | #define TYPE_EMPTY_LIST 7 // marker for empty list, has no data allocated 55 | #define TYPE_ARRAY 8 // array, *data is ElfArray 56 | #define TYPE_BYTES1 50 // bytestring of size 1 57 | #define TYPE_BYTES2 51 // bytestring of size 2 58 | #define TYPE_BYTES3 52 // bytestring of size 3 59 | #define TYPE_BYTES4 53 // bytestring of size 4 60 | #define TYPE_BYTES5 54 // bytestring of size 5 61 | #define TYPE_BYTES6 55 // bytestring of size 6 62 | #define TYPE_BYTES7 56 // bytestring of size 7 63 | #define TYPE_BYTES8 57 // bytestring of size 8 64 | #define TYPE_BYTES 58 // bytestring of arbitrary size (pointer to ElfBytes) 65 | 66 | // PENDING: could define fixed point numbers, with 1-6 decimals 67 | // eg. TYPE_FIX1 means signed 64bit number with 1 decimal (eg. -4205 means 68 | // -420.5) 69 | 70 | 71 | #define ELF_TRUE \ 72 | (ElfVal) { TYPE_TRUE, {} } 73 | #define ELF_FALSE \ 74 | (ElfVal) { TYPE_FALSE, {} } 75 | #define ELF_NIL \ 76 | (ElfVal) { TYPE_NIL, {} } 77 | 78 | #define is_nil(v) ((ElfVal*)v)->type == TYPE_NIL 79 | #define is_true(v) ((ElfVal*)v)->type == TYPE_TRUE 80 | #define is_false(v) ((ElfVal*)v)->type == TYPE_FALSE 81 | #define is_type(v, atype) ((ElfVal*)v)->type == atype 82 | #define long_val(v) (((ElfVal *)v)->data.longVal) 83 | #define double_val(v) (((ElfVal *)v)->data.doubleVal) 84 | #define ptr_val(v) (((ElfVal *)v)->data.ptrVal) 85 | #define bytes_val(v) (((ElfVal *)v)->data.bytesVal) 86 | 87 | #define set_long_val(v, l) (((ElfVal *)v)->data.longVal = l) 88 | #define set_double_val(v, d) (((ElfVal *)v)->data.doubleVal = d) 89 | #define set_bytes_val(v, b) (((ElfVal *)v)->data.bytesVal = b) 90 | #define set_ptr_val(v, p) (((ElfVal *)v)->data.ptrVal = p) 91 | 92 | #define alloc_val() malloc(sizeof(ElfVal)) 93 | #define alloc_cons() malloc(sizeof(ElfCons)) 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /examples.pl: -------------------------------------------------------------------------------- 1 | :- module(examples, [ex/2, ex/3]). 2 | :- use_module(elf_file). 3 | 4 | ex(Name, Code) :- ex(Name, Code, _Result). 5 | 6 | ex("sum list of numbers", 7 | "[4, 2, 0, 6, 9] sum", 8 | 21). 9 | 10 | ex("reverse string", 11 | "\"elf helper\" reverse", 12 | `repleh fle`). 13 | 14 | ex("string digits", 15 | "\"some4digits in this string 2!\" keep(&digit), (_ first * 10) + _ last", 16 | 42). 17 | 18 | ex("format string", 19 | "\"%s day with %d%% chance of rain\" fmt(\"cloudy\",13)", 20 | `cloudy day with 13% chance of rain`). 21 | 22 | ex("if/else", 23 | "ns: [12,5,42,1], 24 | 25 | # if without else returns nil 26 | # nil prints nothing 27 | ns do({($ > 10) if(\"big\") print}), 28 | 29 | \"---\" print, 30 | 31 | # then can be a value or a function 32 | ns do({n:$, (n > 10) if({\"%d is big\" fmt(n) print})}), 33 | 34 | \"---\" print, 35 | 36 | # second arg to if can be specified for else 37 | ns do({ ($ > 10) if(\"big\", $) print })", 38 | nil). 39 | 40 | ex("FizzBuzz", 41 | "1 to(100) do({n| [15,3,5] map({(n % $) = 0}) cond(\"FizzBuzz\",\"Fizz\",\"Buzz\", $) print})", nil). 42 | 43 | ex("FizzBuzz 2", 44 | "1 to(100) do({ 45 | fizz: ($ % 3) = 0, 46 | buzz: ($ % 5) = 0, 47 | (fizz or buzz) if({ fizz if(\"Fizz\") ++ buzz if(\"Buzz\") }, $) print 48 | })", 49 | nil). 50 | 51 | ex("Records", 52 | "# define Elf record 53 | Elf{name, age}, 54 | 55 | # install a method on it 56 | Elf.greet: { \"Hello %s, my name is %s and I'm %d years old!\" fmt($, my name, my age)}, 57 | 58 | # Make an instance 59 | Elf{name: \"Elfo\", age: 666}, 60 | 61 | # increment age 62 | _ age(_ age + 1), 63 | 64 | # Call method on it 65 | _ greet(\"world\")", `Hello world, my name is Elfo and I'm 667 years old!`). 66 | 67 | %% ex("HTML generation", 68 | %% "# Define record for element 69 | %% Elt{tag, attrs, content}, 70 | %% Text{text}, 71 | 72 | %% Text.html: {my text}, 73 | 74 | %% # render as html 75 | %% Elt.html: { 76 | %% \"<%s\" fmt(my tag) ++ 77 | %% my attrs mapcat({\" %s='%s'\" fmt($ first, $ last) }) ++ 78 | %% \">\" ++ 79 | %% my content mapcat(&html) ++ 80 | %% \"\" fmt(my tag) 81 | %% }, 82 | 83 | %% Elt.text: { my content(my content ++ [Text{text: $}]) }, 84 | 85 | %% # Define a dynamic method handler to add child 86 | %% # this works by having elements as methods 87 | %% Elt.dynamic: {method,args| 88 | %% n: Elt{tag: method}, 89 | %% (args len >= 1) if({n attrs(args first)}), 90 | %% (args len = 2) if({args last call(n)}), 91 | %% my content(my content ++ [n]) 92 | %% }, 93 | 94 | %% # Use the HTML generation to output some markup 95 | 96 | %% Todo{label, complete}, 97 | %% todos: [Todo{label:\"simple HTML generation\", complete: true}, 98 | %% Todo{label:\"web framework?\", complete: false}], 99 | %% h: Elt{tag: \"div\"}, 100 | %% _ ul([[\"class\",\"todos\"]], 101 | %% {ul| todos do({ li: ul li([[\"class\", $ complete if(\"complete\", \"active\")]]), 102 | %% li input([[\"type\",\"checkbox\"]] ++ $ complete if([[\"checked\",\"1\"]])), 103 | %% li text($ label) }) }), 104 | %% h html", 105 | %% % FIXME: should support self closing tags, but not important for demo 106 | %% `
  • simple HTML generation
  • web framework?
`). 107 | 108 | ex("Read file lines", 109 | "\"README.md\" lines first", 110 | `# Elf programming language`). 111 | 112 | ex("Import code", 113 | "\"examples/elves.elf\" use, 114 | youngest: elves minw(&age) _1, 115 | youngest greet(\"world\")", 116 | `Hi world! My name is Biscuit Peppermint and I'm 75 years old!`). 117 | 118 | ex("Advent of Code 2023, day 1", Code, [56506, 56017]) :- 119 | file_codes(`examples/aoc2023_day1.elf`, Cs), 120 | string_codes(Code, Cs). 121 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Elf programming language 2 | 3 | ![test workflow](https://github.com/tatut/elf-lang/actions/workflows/test.yml/badge.svg) 4 | 5 | ![elf-lang logo](https://github.com/tatut/elf-lang/blob/main/elf-lang-logo-small.png?raw=true) 6 | 7 | This is the Elf programming language. 8 | 9 | Designed to get help those elves get their xmas duties done 10 | without tripping into lava. 11 | 12 | Try the playground at: https://tatut.github.io/elf-lang/ 13 | 14 | ## Syntax and evaluation 15 | 16 | Syntax is a little bit Smalltalkish, with Perlish implicit values and Clojurish 17 | functional programming with records & methods.... all implemented lovingly in Prolog. 18 | 19 | Like Smalltalk all method calls are evaluated first left to right, then all binary operators left to right. 20 | There is no operator precedence, use parenthesis to group math expressions. 21 | 22 | ``` 23 | # wrong appends 5 (length of string "World") to "Hello " 24 | "Hello " ++ "World" len 25 | 26 | # right, calculates length of "Hello World" (11) 27 | ("Hello " ++ "World") len 28 | ``` 29 | 30 | **Why?** 31 | 32 | Why not. 33 | 34 | 35 | ### Syntax 36 | 37 | **Syntax overview** 38 | 39 | Comments start with `#` and end in newline. 40 | 41 | ``` 42 | "I'm an Elf" # a string obviously (actually a list of ascii code ints) 43 | `(c|h)at` # a compiled regular expression 44 | 45 | @? # the ´?´ character (as ascii integer 62) 46 | 47 | 420.69 # a decimal number 48 | 49 | 666 # an integer 50 | 51 | [4, 6, 9] # list of integers 52 | 53 | true # the true value 54 | false # the false value 55 | nil # the nothing value 56 | 57 | $ # reference to the 1st argument in function 58 | $1 ... $9 # reference to the Nth argument in function 59 | my # reference current object in methods 60 | &foo # reference to method named foo 61 | 62 | _ # reference to the value of the last statement 63 | 64 | { $ * 2 } # function of arity 1 65 | 66 | {a,b| a + b} # function of arity 2 with named arguments 67 | 68 | 69 | ["hello", "there"] # list 70 | 71 | Elf{name,age} # object record definition 72 | Elf.greet: { "Hello %s, my name is %s." fmt(my name, $) } # define method on record 73 | 74 | elf greet("world") # call method 75 | 76 | Elf{name: "Jolly Twinkletoes", age: 1607} # an object 77 | 78 | Elf new("Scrappy Fairytoes", 666) # programmatic construction 79 | 80 | %{"foo": 42} # associative key/value map 81 | 82 | the_answer: 42 # assignment statement 83 | 84 | "naughty_list.txt" lines # call method lines on string 85 | 86 | # if is just a method taking 1 or 2 args (then,else) 87 | # if the value is a function it is called with the value 88 | # otherwise it is returned 89 | somebool if(thenblock) 90 | somebool ife(thenblock, elseblock) 91 | 92 | ``` 93 | 94 | ### Valid names 95 | 96 | Names of any methods or assignments must begin with `_` or a letter (case doesn't matter), 97 | after that it may contain alphanumeric and `-`, `_` and `?` characters. 98 | 99 | For example these are valid names: 100 | - `my_variable` 101 | - `is-valid?` 102 | - `Foo1` 103 | 104 | Reserved words (`and`, `or` and `_`) cannot be used as names. 105 | 106 | ### Binary operations 107 | 108 | Elf supports the following binary operators: 109 | - `+`,`-`,`*`,`/`,`%` numeric addition, substraction, multiplication, division and modulo (number) 110 | - `<`, `<=`, `>`, `>=` numeric less than, less than or equals, greater than and greater than or equals (true/false) 111 | - `=` value equality (true/false) 112 | - `++` list append (yields list that is the appended left and right hand side lists) 113 | - `and` boolean truth (true if both left and right hand sides are truthy, false otherwise) 114 | - `or` boolean or (true if either left or right hand side is truthy, false otherwise) 115 | 116 | ### Records 117 | 118 | Elf supports records which have a predefined list of fields. 119 | Records can be instantiated by `RecordName{field1Name: field1Val, ...}` any fields not given a value will have the initial value `nil`. 120 | 121 | Record fields are automatically added as method to get/set the value. 122 | The 0-arity method gets the value and 1-arity sets it. 123 | 124 | Records also support user defined methods that are functions with the 125 | special name `my` referring to the record instance. 126 | 127 | There is a special method called `dynamic` that will be invoked for any 128 | methods not defined. This can be used for meta-programming. 129 | The method gets the name of the undefined method as string and a list 130 | of its arguments. 131 | 132 | ### Regular expressions 133 | 134 | Elf supports regular expressions that are read by using backticks. 135 | A regular expression is callable like a function and returns a list of 136 | strings (first one being the full match and then any capture groups) 137 | or `nil` if the input does not match the regex. 138 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 69 | 70 | 164 | 165 | 166 | 167 |

elf-lang playground

168 |
169 |
170 |

Code 171 | 174 |

175 |
[1, 2, 4] sum * 3
176 |
177 | 178 |
179 |

Evaluation result

180 |
evaluate code to see result here
181 |
182 |
183 |

Printed output

184 |
185 |
186 |
187 | Method reference 188 | 189 | 190 | -------------------------------------------------------------------------------- /methods.html: -------------------------------------------------------------------------------- 1 | 2 | Elf method reference

Elf methods

NameDoc
_0Return 1st value of recipient.
_1Return 2nd value of recipient.
_2Return 3rd value of recipient.
absReturn the absolute value of recipient.
allpairsReturn all possible [A,B] lists where A and B are elements of recipient list.
atat(Key) 3 | Return value at key or nil if not present.
callCall recipient function with given arguments.
ceilReturn smallest integer that is greater than or equal to recipient.
condcond(Action1,...,ActionN, Else) 4 | Takes the first truthy value in recipient and runs the corresponding action or Else if no value is truthy. If Else is not specified, returns nil when no value is truthy.
contentsReturn contents of recipient file as string.
countcount(Fn) 5 | Count the number of values in recipient where Fn returns truthy.
debugPrint internal interpreter state.
decReturn the recipient decremented by 1.
digitReturn ASCII digit code as number, or nil if code is not a digit.
dodo(Fn) 6 | Call Fn on each value in recipient, presumably for side effects. For maps, Fn is called with key and value as arguments. Returns nil.
dropdrop(N) 7 | Return recipient without the first N values.
dropwdropw(Pred) 8 | Return items of recipient after the first Pred call on item returns falsy.
elseelse(V) 9 | Return recipient value if it is truthy, otherwise return V.
empty?True if recipient is nil, the empty list or an empty map.
ends?ends(Suffix) 10 | Returns true if recipient ends with Suffix, false otherwise.
evalEval recipient as elf code, returns the last value.
filterfilter(Fn) 11 | Call Fn on each value in recipient, return list of values where the result is truthy.
firstReturn first value in recipient.
floorReturn largest integer that is less than or equal to recipient.
fmtfmt(Arg1,...,ArgN) 12 | Format recipient string with given parameters, any use "%s" and "%d" to mark argument strings and numbers.
foldfold(Fn, Init) 13 | Call Fn with Init and first value of recipient, then with successive return values and values in recipient until all values are processed. If Init is omitted, nil is used.
groupgroup(Fn) 14 | Group values in recipient by Fn. Calls Fn on each value in recipient and returns map with the result as key and list of values having that result as value.
has?has?(Item) 15 | True if Item is member of recipient list, false otherwise.
headsReturn successive heads of recipient.
ifif(Then) 16 | If recipient is truthy, return Then. If Then is a function, it is called with the recipient as argument and its return value is returned.
ifif(Then,Else) 17 | If recipient is truthy, return Then otherwise return Else. Both can be values or functions.
in?in?(List) 18 | True if recipient is a member of List, false otherwise.
incReturn the recipient incremented by 1.
indexindex(Elt) 19 | Returns the first index of Elt in recipient list or -1 if it does not appear in the list.
joinjoin(Sep) 20 | Join recipient list of lists into one list with separator.
keepkeep(Fn) 21 | Call Fn on all values in recipient, returns list of all results that are not nil.
lastReturn last value in recipient.
lenReturns the length of a list.
linesReturn list of lines in file.
list?True if recipient is a list, false otherwise.
mapmap(Fn) 22 | Call Fn on all values in recipient and returns values as a list.
map?True if recipient is a map, false otherwise.
mapcatmapcat(Fn) 23 | Call Fn on all values in recipient appending all resulting lists in one list. Fn must return a list or nil.
matchmatch(Input) 24 | Parse input by matching it to specs in recipient list. Each spec can be a list expected at this position or a function that parses from the current position. Parsing function must return a parsed value and the rest of the list. Returns list of all non-nil values parsed by parsing functions.
maxReturn the maximum value of a list of numbers.
maxmax(Fn) 25 | Call Fn on each value in recipient, return the maximum value.
maxwmaxw(Fn) 26 | Call Fn on each value in recipient, return list containing the largest result and the value it corresponds with.
minReturn the minimum value of a list of numbers.
minmin(Fn) 27 | Call Fn on each value in recipient, return the minimum value.
minwminw(Fn) 28 | Call Fn on each value in recipient, return list containing the smallest result and the value it corresponds with.
nil?True if recipient is nil, false otherwise.
notReturn negated boolean of recipient.
nthnth(N) 29 | Return the Nth value in recipient.
number?True if recipient is a number, false otherwise.
numdigitsReturn the number of digits in a number, eg. 123 => 3
partpart(Size,[Skip]) 30 | Partition list into sublists of Size. If skip is omitted it is the same as Size. If skip is omitted, the last list may be shorter than the others if the input is not evenly sized. 31 | 32 | Example: 33 | [1,2,3,4] part(2,1) 34 | => [[1,2],[2,3],[3,4]] 35 |
powpow(N) 36 | Raise recipient to Nth power.
prPretty print recipient. Returns recipient.
printPrint value to standard output.
putput(Key1,Val1,...,KeyN,ValN) 37 | Put values into map, returns new map with added mappings.
readRead an Elf value (number, string, boolean or nil) from recipient string. Returns a list containing the read value and the rest of the string.
refCreate new mutable reference from recipient.
reverseReturn recipient reversed.
roundReturn the closest integer to recipient.
signReturn -1, 0 or 1 if recipient is negative, zero or positive respectively.
somesome(Pred) 38 | Returns the first value in recipient where the result of calling Pred on it returns truthy, or nil otherwise.
sortReturn recipient as sorted.
sortsort(Fn) 39 | Call Fn on each value in recipient, return recipient sorted by the return values.
sortuReturn recipient as sorted without duplicates.
splitsplit(Sep) 40 | split recipient list into sublists at each occurence of separator Sep.
splitwsplitw(Pred) 41 | Combines takew and dropw. Return list of [taken, dropped].
starts?starts?(Prefix) 42 | Returns true if recipient starts with Prefix, false otherwise.
strReturn readable representation of recipient as string.
sumReturns sum of all values in recipient.
swapswap(Fn,...Args) 43 | Update value of ref by calling Fn on its current value (and optional Args). Returns new value.
taketake(N) 44 | Return the first N values of recipient.
takewtakew(Pred) 45 | Return items of recipient list while calling Pred on item returns truthy.
toto(Upto) 46 | Return list of successive numbers from recipient to Upto.
toto(Upto, Skip) 47 | Return list of successive numbers from recipient to Upto, incrementing with Skip.
useLoad file denoted by recipient as elf code. 48 | All record types, methods and names defined in the file are available after this call.
valGet or set current value of mutable reference.
whilewhile(Then) 49 | Call recipient fn repeatedly while it returns a truthy value, call Then with that value.
wsParsing method to skip 0 or more whitespace characters. Returns [nil, rest] where rest is the string after the whitespace.
-------------------------------------------------------------------------------- /elf_scanner.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "elf.h" 7 | #include "elf_scanner.h" 8 | 9 | #define next(s) { s->at++; s->column++; } 10 | #define cur(s) *s->at 11 | 12 | bool is_alpha(char ch) { 13 | return (ch >= 'a' && ch <= 'z') || 14 | (ch >= 'A' && ch <= 'Z'); 15 | } 16 | 17 | bool is_digit(char ch) { return ch >= '0' && ch <= '9'; } 18 | 19 | bool is_alphanumeric(char ch) { return is_alpha(ch) || is_digit(ch); } 20 | 21 | bool is_identifier(char ch) { 22 | return is_alphanumeric(ch) || 23 | ch == '_' || ch == '?'; 24 | } 25 | bool is_identifier_start(char ch) { 26 | return is_alpha(ch) || ch == '_' || ch == '?'; 27 | } 28 | 29 | bool looking_at_word(Scanner *s, char *word) { 30 | char *at = s->at; 31 | size_t c = 0; 32 | while(*word != 0) { 33 | if(*at == 0) return false; 34 | if(*at != *word) return false; 35 | at++; 36 | word++; 37 | c++; 38 | } 39 | if(!is_identifier(*at)) { 40 | /* source does not continue with an identifier char, 41 | we found it */ 42 | s->at = at; 43 | s->column += c; 44 | return true; 45 | } else { 46 | return false; 47 | } 48 | } 49 | 50 | // Read literal ElfVal, allocates new value 51 | Token literal(size_t line, size_t col, ElfVal literal) { 52 | return (Token) { LITERAL, line, col, literal }; 53 | } 54 | 55 | // Read identifier, identifier name is interned (FIXME: doit) 56 | Token identifier(Scanner *s) { 57 | char *at = s->at + 1; 58 | while(is_identifier(*at)) { 59 | at++; 60 | } 61 | size_t sz = at - s->at; 62 | char *id = malloc(at - s->at + 1); 63 | id[sz] = 0; 64 | memcpy(id, s->at, sz); 65 | //printf("allocate identifier of len: %ld\n", sz); 66 | Token t = { IDENTIFIER, s->line, s->column }; 67 | t.data.name = id; 68 | s->at = at; 69 | s->column += sz; 70 | return t; 71 | } 72 | 73 | void skip_comment(Scanner *s) { 74 | // comment until the end of line 75 | while(*s->at != '\n') s->at++; 76 | s->at++; 77 | s->line++; 78 | s->column = 1; 79 | } 80 | 81 | Token number(Scanner *s) { 82 | long f = 1; 83 | size_t col = s->column; 84 | if(cur(s) == '-') { 85 | f = -1; 86 | next(s); 87 | } 88 | long num = 0; 89 | while(is_digit(cur(s))) { 90 | num *= 10; 91 | num += cur(s) - '0'; 92 | next(s); 93 | } 94 | if(cur(s) == '.') { 95 | next(s); 96 | // fraction 97 | double fr = 1; 98 | double frac = 0; 99 | while(is_digit(cur(s))) { 100 | fr *= 10; 101 | frac = 10*frac + (cur(s) - '0'); 102 | next(s); 103 | } 104 | ElfVal v; 105 | v.type = TYPE_DOUBLE; 106 | set_double_val(&v, (double) f * ((double) num + frac/fr)); 107 | return literal(s->line, col, v); 108 | } else { 109 | ElfVal v; 110 | v.type = TYPE_INT; 111 | set_long_val(&v, num * f); 112 | return literal(s->line, col, v); 113 | } 114 | return (Token) { ERROR }; // FIXME 115 | } 116 | 117 | /** 118 | * Read string in as list of integers. 119 | * Allocates cons cells for each item. 120 | * If the string is empty, returns the empty list marker value (without allocating anything). 121 | */ 122 | Token string(Scanner *s, size_t line, size_t col) { 123 | // String is actually a list of integers 124 | ElfVal val; 125 | ElfCons *head = NULL; 126 | val.type = TYPE_EMPTY_LIST; 127 | 128 | next(s); // skip first '"' 129 | while(cur(s) != '"') { 130 | long ch = cur(s); 131 | if(head == NULL) { 132 | // no head yet, make it and change type to non-empty list 133 | val.type = TYPE_LIST; 134 | head = alloc_cons(); 135 | val.data.ptrVal = head; 136 | head->value.type = TYPE_INT; 137 | head->value.data.longVal = ch; 138 | } else { 139 | head->next = alloc_cons(); 140 | head->next->value.type = TYPE_INT; 141 | head->next->value.data.longVal = ch; 142 | 143 | head = head->next; 144 | } 145 | next(s); 146 | } 147 | 148 | return literal(line, col, val); 149 | 150 | } 151 | /* Return next token from scanner, 152 | Modifies scanner state. */ 153 | Token scan(Scanner *s) { 154 | for(;;) { 155 | size_t line = s->line, col = s->column; 156 | char ch = *s->at; 157 | char nextch = *(s->at+1); 158 | TokenType c; 159 | switch(ch) { 160 | // end reached 161 | case 0: case -1: 162 | return (Token) { EOS, line, col }; 163 | 164 | // single char tokens 165 | case '[': next(s); return (Token) { BRACKET_OPEN, line, col }; 166 | case ']': next(s); return (Token) { BRACKET_CLOSE, line, col }; 167 | case '(': next(s); return (Token) { PAREN_OPEN, line, col }; 168 | case ')': next(s); return (Token) { PAREN_CLOSE, line, col }; 169 | case '{': next(s); return (Token) { BRACE_OPEN, line, col }; 170 | case '}': next(s); return (Token) { BRACE_CLOSE, line, col }; 171 | case ':': next(s); return (Token) { ASSIGN, line, col }; 172 | case '|': next(s); return (Token) { BAR, line, col }; 173 | case '=': next(s); return (Token) { EQUAL, line, col }; 174 | case ',': next(s); return (Token) { COMMA, line, col }; 175 | 176 | // comments and whitespace skips 177 | case '#': skip_comment(s); break; 178 | case '\n': s->at++; s->line++; s->column = 1; break; 179 | case ' ': case '\r': case '\t': next(s); break; 180 | 181 | // binary operators 182 | case '*': next(s); return (Token) { MULTIPLY, line, col }; 183 | case '/': next(s); return (Token) { DIVIDE, line, col }; 184 | case '+': 185 | next(s); 186 | if(nextch == '+') { 187 | next(s); 188 | return (Token) { CONCAT, line, col }; 189 | } 190 | return (Token) { ADD, line, col }; 191 | case '-': 192 | if(is_digit(nextch)) 193 | return number(s); 194 | else { 195 | next(s); 196 | return (Token) { SUBTRACT, line, col }; 197 | } 198 | case '%': 199 | next(s); 200 | if(nextch == '{') { 201 | next(s); 202 | return (Token) { DICT_OPEN, line, col }; 203 | } 204 | else { 205 | return (Token) { MODULO, line, col }; 206 | } 207 | case '<': case '>': // comparison 208 | c = (ch == '<' ? LESSER_THAN : GREATER_THAN); 209 | next(s); 210 | if(nextch == '=') { next(s); c++; } // increment to OR EQUAL variant 211 | return (Token) { c, line, col }; 212 | 213 | // reserved words (true, false, nil) 214 | case 't': 215 | if(looking_at_word(s, "true")) { 216 | return literal(line, col, ELF_TRUE); 217 | } else { 218 | return identifier(s); 219 | } 220 | case 'f': 221 | if(looking_at_word(s, "false")) { 222 | return literal(line, col, ELF_FALSE); 223 | } else { 224 | return identifier(s); 225 | } 226 | case 'n': 227 | if(looking_at_word(s, "nil")) { 228 | return literal(line, col, ELF_NIL); 229 | } else { 230 | return identifier(s); 231 | } 232 | 233 | // literal string 234 | case '"': return string(s, line, col); 235 | 236 | // numbers and identifiers 237 | default: 238 | if(is_identifier_start(ch)) { 239 | return identifier(s); 240 | } else if(is_digit(ch)) { 241 | return number(s); 242 | } 243 | return (Token){ ERROR }; 244 | } 245 | } 246 | } 247 | 248 | Scanner *scanner_new(char *input) { 249 | Scanner *s = malloc(sizeof(Scanner)); 250 | s->at = input; 251 | s->line = 1; 252 | s->column = 1; 253 | s->source = input; 254 | return s; 255 | } 256 | 257 | void scanner_free(Scanner *s) { free(s); } 258 | 259 | void token_free(Token t) { 260 | // do nothing now, we should intern names 261 | if(t.type == LITERAL && t.data.val.type == TYPE_LIST) { 262 | // PENDING: should the interpreter take ownership? so we don't free this 263 | 264 | } 265 | } 266 | 267 | // Unit tests 268 | #ifdef TEST 269 | 270 | #include "test.h" 271 | 272 | Scanner s_(char *source) { return (Scanner){source, source, 1, 1}; } 273 | 274 | #define tok(str) \ 275 | { \ 276 | s = s_(str); \ 277 | t = scan(&s); \ 278 | } 279 | 280 | #define nexttok() \ 281 | { t = scan(&s); } 282 | 283 | void test() { 284 | Scanner s; 285 | Token t; 286 | printf("sizeof elfdata: %ld\nsizeof token: %ld\n", sizeof(ElfVal), sizeof(Token)); 287 | testing("Booleans", { 288 | it("reads true", { tok("true"); 289 | assert(t.type == LITERAL && is_true( &t.data )); 290 | }); 291 | it("reads false", { tok("false"); 292 | assert(t.type == LITERAL && is_false( &t.data )); 293 | }); 294 | it("doesn't read too long", { tok("trueness"); 295 | assert(t.type == IDENTIFIER && 296 | strcmp(t.data.name, "trueness")==0) 297 | }); 298 | it("skip ws", { tok("\n\n true"); 299 | assert(t.line == 3 && t.column == 4 && is_true(&t.data)) 300 | }); 301 | it("skips comment", { tok("# true true, so true\n false"); 302 | assert(t.line == 2 && t.column == 3 && is_false(&t.data)); 303 | }); 304 | }); 305 | 306 | testing("Numbers", { 307 | it("reads positive number", { 308 | tok(" 42069 "); 309 | assert(t.type == LITERAL && is_type(&t.data, TYPE_INT) && 310 | long_val(&t.data) == 42069) 311 | }); 312 | it("reads negative number", { 313 | tok("# very cold temp\n-273"); 314 | assert(t.type == LITERAL && is_type(&t.data, TYPE_INT) && 315 | long_val(&t.data) == -273) 316 | }); 317 | it("reads float number", { 318 | tok("420.69"); 319 | assert(t.type == LITERAL && is_type(&t.data, TYPE_DOUBLE) && 320 | double_val(&t.data) == 420.69); 321 | }); 322 | 323 | it("reads negative float", { 324 | tok("-3.1415666"); 325 | assert(t.type == LITERAL && is_type(&t.data, TYPE_DOUBLE) && 326 | double_val(&t.data) == -3.1415666); 327 | }); 328 | }); 329 | 330 | testing("Binary operators", { 331 | it("reads *", { 332 | tok(" * "); 333 | assert(t.type == MULTIPLY); 334 | }); 335 | it("doesn't confuse - with negative number", { 336 | tok(" -foo"); 337 | assert(t.type == SUBTRACT); 338 | nexttok(); 339 | assert(t.type == IDENTIFIER && strcmp("foo", t.data.name)==0); 340 | }); 341 | it("reads concat", { 342 | tok("++"); 343 | assert(t.type == CONCAT); 344 | }); 345 | }); 346 | 347 | testing("Comparison operators", { 348 | it("reads < and <=", { 349 | tok("< <="); 350 | assert(t.type == LESSER_THAN); 351 | nexttok(); 352 | assert(t.type == LESSER_THAN_EQUAL); 353 | }); 354 | it("reads > and >=", { 355 | tok("> >="); 356 | assert(t.type == GREATER_THAN); 357 | nexttok(); 358 | assert(t.type == GREATER_THAN_EQUAL); 359 | }); 360 | it("reads =", { 361 | tok("="); 362 | assert(t.type == EQUAL); 363 | }); 364 | }); 365 | 366 | testing("parens, braces, brackets, bar, comma and assign", { 367 | it("reads ({[|,:]})", { 368 | tok("({[|,:]})"); 369 | assert(t.type == PAREN_OPEN); nexttok(); 370 | assert(t.type == BRACE_OPEN); nexttok(); 371 | assert(t.type == BRACKET_OPEN); nexttok(); 372 | assert(t.type == BAR); nexttok(); 373 | assert(t.type == COMMA); nexttok(); 374 | assert(t.type == ASSIGN); nexttok(); 375 | assert(t.type == BRACKET_CLOSE); nexttok(); 376 | assert(t.type == BRACE_CLOSE); nexttok(); 377 | assert(t.type == PAREN_CLOSE); nexttok(); 378 | assert(t.type == EOS); 379 | }); 380 | }); 381 | 382 | testing("strings", { 383 | it("reads the empty string", { 384 | tok("\"\""); 385 | assert(t.type == LITERAL && t.data.val.type == TYPE_EMPTY_LIST); 386 | }); 387 | it("reads string", { 388 | tok("\"hello\nworld!\""); 389 | assert(t.type == LITERAL && t.data.val.type == TYPE_LIST); 390 | ElfCons *head = t.data.val.data.ptrVal; 391 | size_t len = 1; 392 | assert(head->value.type == TYPE_INT && head->value.data.longVal == 'h'); 393 | while(head->next != NULL) { 394 | head = head->next; 395 | len++; 396 | } 397 | assert(len == 12); 398 | assert(head->value.type == TYPE_INT && head->value.data.longVal == '!'); 399 | }); 400 | }); 401 | } 402 | 403 | #endif 404 | -------------------------------------------------------------------------------- /examples/aoc2023_day1.txt: -------------------------------------------------------------------------------- 1 | nkzjrdqrmpztpqninetwofour1znnkd 2 | s5sevenxrdfr4mhpstgbjcfqckronesix 3 | 3four4 4 | sfdrtpvspsixsn5zbqmggb8vgkjseight 5 | 72666gxzflnsfqmndjdscvqmcqls5 6 | hvqgbssr46four1cdcjxcdcp 7 | zpjvvcbeightthree1bcnvjzbnqseven 8 | jqbjxdkkveightrtktnsr92sevenmztdg 9 | three3ninefive 10 | twosevennnine6467 11 | fjrzk9rfpqtbrc1bzfclczqxq 12 | ninegkftwo29seven 13 | eightone7threenl7mtxbmkpkzqzljrdk 14 | twothree6vpnvvnshn 15 | vvhtbjjrr6xghmzf 16 | bfjtdslkdbthree4jvvonezqdthreesrghnnbsix 17 | kkbhh5fivepvhzhdsvxvnkrn4 18 | 4qsqr235twogl21 19 | v2seventhreezjfour6 20 | 38gfqdpkfhdonespqbckbgkkzhgnbqgslkhfl7 21 | dtwoneeight6llzcxssgrdfjmjvfbvtwo9 22 | sixsixgnpprvjdkgvqmr1 23 | 7sqthfchpjklpn 24 | foureight7hl111rznjfh 25 | 9six2threefiversbsc 26 | 8six1ninezjsix 27 | 6threezjmclknqcztwocfiveninextpdq1 28 | gsdsr2seven51 29 | vtgdx5 30 | bkmsrzq71vtkrmjqcvfourheight 31 | eightnine9six 32 | 85mklcmpx1 33 | six3four 34 | five5hqbppzmszhzthmh1 35 | 2ctfonekpns 36 | llljztzffjlsktwogsjgbzvsp8ninefourg 37 | 4rplhrnglj7oned35 38 | sevenqnlbrpgone7 39 | bcmvrhbgfourcrgxrseven4eight1ptmfzhpqcnine 40 | rkqztsqj2cjfsbrvcmrsixhgbhdone 41 | two2zfrcfhsl 42 | fourfournxdsfx6eightnine9 43 | v4ninecdtwohxmpj 44 | 2378hcrs 45 | shhbgk74sevenrzknghzsk 46 | 4sevendeight2 47 | bvjdfthreefive4pfxfvcxc6seven4zjkxdtcjr 48 | 2seven4three239kmjzfhbgxxmqv 49 | sixfourd7 50 | hxkkdnjkbmftmone1npmqbnx 51 | rsxvqjbqx2bzprkml2 52 | 8twothree8dzqbrvdx5 53 | xqm9 54 | 6six8dnfbs9qlxkfrcrckmfrjgcxg 55 | 822 56 | eightmbhfrxztcgqbfj6threetwo 57 | five8grcllvz 58 | oneninefive2klzgbdvvjbgnxkrp 59 | 3bvlpmmltpr9zpxmbgslfhgtmtx 60 | 16439 61 | 5lglzqf2sgvshzjdmpzxldnkgzlrbvvmfsk 62 | four4rjrkzvfive2cfl7fourfive 63 | eightv59six7zrczcnkfivejpqxx 64 | hzgrkrbmjmzhpfkfgg5 65 | 5rjvdsfourtwo8 66 | 938cmftxkv8sixseven 67 | 973threenjtl4two5 68 | 4r9gpfmjnseven 69 | threesixmmbkxpm7six69v 70 | two369j 71 | 5964fttlsfhjk 72 | 14ppncqfoureighttwo8nnktcn 73 | 3gdfclsxmrfourcfznxqnceighttwo 74 | 2cvtbpsbbhbzrxmp 75 | 967 76 | 2five7xrbfrpjdn6kkrdxt4cqfnhlkl 77 | vtpbxjl3 78 | sixprfsqseightsevendonerxmphzl6cgtpgbgtjs 79 | 32ninethree 80 | 59hngnqrmqjbbtrtrzplg 81 | sevenone39sixsix41 82 | feightwo5 83 | 8sixsixrcbvkllpjsnbcnvvsktwo7one 84 | 2fourleightdsvskbpmlfour 85 | cdfkddlnine34frsshqsevensevenfourqf 86 | 89clszfjqlffive5s 87 | 1tdjxtz723zkhptcxnine 88 | czt7hkjdbpkpdvfourthree4cfmqhxfcxz6 89 | 76twohglrflhmmqkxrpfszpxhjs56 90 | 4sixpfv 91 | fivetqbfjxsjzp7 92 | fiveh4one9qkgnqf 93 | 6czjmgdlckr6sevenonegvtpmgz6 94 | 52one 95 | four53twofivefivethree7 96 | four69sevenjkfqnhqlfkcnfive 97 | kpxr4nxcmjcv21599 98 | 1mvgdm7xxnqzptrphg 99 | onezmhzdlqczk82eight 100 | 94599twonine 101 | four9nine 102 | plbrvdlmfjqeight329nine 103 | 8dhpq2bztlqctgzqhkqtlphnkrgpdmtwoeight 104 | eightddscnrvshonexntjctspnnsix1 105 | hxjhsc4fivexcbjbsix 106 | 863 107 | nine95fivethreer6 108 | two7tcnzbbqnfr427 109 | threethreeeightzvfsznine5eight7 110 | crvhlfone7xsqhkshpsix2nine73oneighttq 111 | qcqzrfgninejkrnbdg9two7zbsglmznkjseven4 112 | nqvskpgnlk1fourrhnzrf4 113 | 4nhvxsstxvtndbpmntpkmeight6 114 | 3fivenine18 115 | rloneight124 116 | xrzk8nine 117 | lzhpftwo3qqzkjhxjdt 118 | 8nine6 119 | 8fgxllqqpcffive29jfdpf 120 | sixnine57shvscktg46 121 | nineqjznjltskmq98mfour 122 | nine9gzglcmhccnone 123 | 66trvcdsnzn 124 | onevzsznhrvxmpkpxgfourfphttxc2 125 | pfkcvxgmjfournine4cjtpqbsnsixgctm2 126 | 3nj7fivegmgbbfrhf 127 | 9two479sevenn7fqjk 128 | three4193 129 | 622eight9gncbvhjvglgmb 130 | fvbsmrrxmjthree43 131 | 3cpn4xk 132 | 7five6six814nineoneightqbb 133 | mxc91eight 134 | mpbxhspkfgslgptm6 135 | fpfkn7dbpsjxksnk6xkqkfhthree 136 | rl5flvbsbnqnfgggkq7 137 | lt4fivebvhtvld7ninetwohnplchrbvdmf 138 | one3dgvngvtwoeightonefivefive 139 | 27sevenvkmqnmqtwochcchnpkeightrgcpdsvvrg 140 | rr985 141 | 5csrtvjmjzs391sixtwonef 142 | ninepsdseven23 143 | four48eightfourtwo 144 | twothree4fivetwo4lxscf 145 | 856twoqhsjtonebqrlkzqn1cd 146 | sxvd5t7 147 | prkjfourhpdpxlvlnmz6onetl1 148 | 6one3cgxhd 149 | nzeightwofourmz4 150 | lfrnfdkgsixeight2hnlzksmjrsqjmvnmk 151 | 4jddtplseven 152 | four7onefivenine16 153 | 93qd9svlnnskq8eightthree 154 | bxsrknfournhqmqzjxmsix5nineb 155 | 9eightfour 156 | five9jpzgnggzjpsk42dx 157 | 9ht5bxqkdmxmbfonesix 158 | 771f412n6 159 | tpxmjxd28onefrn9hnmztsmxsmctpdxjh 160 | mcmtzjcgmrlgqqrgvc53one9 161 | 6sevensix637 162 | hp5fivetwohqnineeightonedxbdzzlnv 163 | twoone89 164 | 2bsrxhzdsix8threefive7zv 165 | 1sevenfivevjsixqhzlf35 166 | mcplj8fxzgcfivejstdjq 167 | 8two9gvpqzplflhlkjbtvskcfsixthreeeight 168 | 25five25s71four 169 | 2one3eighteighteight 170 | ps75three6jxdb5 171 | 35fourfivexxqkrbb 172 | 35hjvkldnxhdtknflseven68pvtjt 173 | rxs5hvjcngzzmjzvthreej9gsxgfkbf 174 | 87n239jclzrttkmx4 175 | four98nine 176 | eightsix3nine3pqgtwo 177 | 4hbpqhgone3ncnine 178 | 7pfgdmpsn1sixglfvt 179 | 4khktqfqjdqssjpnhmtgffour7hzztx 180 | 3n 181 | six9jhnloneightf 182 | 1ksevensixzmrhsszlngfiveztbkqs 183 | foureightfhvrmq8three7 184 | frzmprnh6z 185 | 1fck1ngpninetdpninefive 186 | 9hgfivegtnxfsptpgvmqjrxp 187 | 9nine8ktxtnrlfourvhrseven52 188 | fiveeightjjqninetwoseven3 189 | 7pbmll4two 190 | sevenlsvddqsixsixbkmtx9njqf1nine 191 | h28rtrjz8dctpxdnh 192 | two12fjonenineszxmj 193 | seven49ltnsm 194 | jqsix6two 195 | 54sixhxzkbh 196 | kg2msbkrtltwo9 197 | 8xplcnjxfthreeeightthree 198 | vdxvvtone81one2 199 | 93five36nine5pcbsfr4 200 | fourfour3six7tj7hgdms 201 | 4dnlcnlxlhzblvkjhzpt95fiveggkv 202 | 48four8 203 | tf71pkcknhlnxhrthreetwo 204 | 7sixsevenm 205 | hgtkrfcf9 206 | jlzxqbsix4qsixtzrg 207 | 1mfqldeightonethreeonetwo 208 | 2eight2bvrltnrthree81fouroneightjfc 209 | 4b4nbtvcnczgpdvqpjqfqstzvctkxneightsgtmm 210 | ninexxcmnhjgs8eightone86 211 | 7eightfiveeightfoursixthreernhsvc 212 | five9vnnqjjklveight2six632 213 | four4qksevennine 214 | threesixfiveqfive3 215 | 8eight8eight4srcc6seven 216 | four13onenineffhhdsmkmq6 217 | 2eightghhrz4gqbsgqz2sevenfour 218 | pnrsdbmbthsix5hrpfrqkdfgone 219 | xsix1tctsixtwonevz 220 | 84qhpkmbjnsb2sevengonetwoppgqxtn 221 | 96223 222 | 9bk 223 | zggvthree2nineklhzrltwocxtkgbdt 224 | dxbpjgoneqhrmqqmpdcfl31 225 | 9ph8gtzxkzjqmvmfk 226 | lqg6 227 | five83sevensix3nine 228 | nine1three 229 | 15eight131blnjkteightworr 230 | xgdmcrgfkqeightsevensevenflzvzcss1 231 | 86nine5four 232 | 1six5bsl4 233 | nrkkzhktjz77threetvmrklnhvfqkfqgbczlr 234 | xpdbfhpxns6zcsphqdtt34nqp 235 | 8twoeight552mgvtxcf 236 | 9threethreetwovrzkcgzhthreetwo 237 | 6onexjcxschzgb3468 238 | 7hxtwo 239 | fzxmfkhbzsprgbx97vxlzzrkn 240 | ninexdvvnseven4 241 | eight8fourfoureighttwo7hpvmjfdgcjcrp 242 | 5lqdlzqcqzmtgdfrpg6xbtgn 243 | 4rphgjktmf4 244 | 84threekclvc1twonine 245 | 2tfsmnzh3qxpcknskhdjhfppc 246 | 7rjzbvjjfmbfivesevenvlrgxxdvk 247 | 5tzseven8 248 | 37rssqhthreesevenone 249 | rdfivebtsbtrqmqm6 250 | 9nine9fourzr4n2 251 | 54kddhldrdvdxggxpkeight2 252 | five4lgtgpcrj 253 | 8gdqbjxdfsf 254 | five4ttz 255 | 8fourjrjvdhnpclgjc 256 | 957five 257 | 3fiveseven 258 | xhkmsrdrxvqshclfdlninebvjzsgfrmlkxlfour91 259 | 9eight1one9oneshm 260 | sixctmfxcpxvxlkcpr3threeoneh 261 | 3nine7two 262 | mmthreecstffvvqx8vbmlmpkxsm 263 | djlxhn48gvs 264 | kpkpx1onenine 265 | 67two75nine5twotczxddjz 266 | mtstwone8 267 | cjfzmrjfk2jzlnqcffsevenfivethreeseven 268 | ckqqbeight9tk 269 | 5threetwo9 270 | 4ninesix 271 | 8rvvrsxcbjxgtcczcrcnhx1 272 | sixnine67gzlcqbvn9six 273 | six7fourthree9 274 | ghpsh6mtfcxjfbqsdzmzqjssix2zkjrrhrd 275 | 5ninefive 276 | fourseven5 277 | 7onepmgddpngxseven1 278 | kcbzf9 279 | mqgoneightgrhp82 280 | fgnsfour7two 281 | tc2nnrqmtpqqxfour6 282 | twofour6eightseven8tworccql 283 | two72scpqmfdvone 284 | 41four87fivefoursix7 285 | ninetvpgphtp27jxxnjtqdlqtjpfnsix 286 | 97vrbqtrfgjmrrhrshzr 287 | 2fgbrbzmv5one4fivefive1eight 288 | lb65xfdkjfivegcgrr 289 | 5ffpxzhzcnmzdtsix 290 | jhhklldghbrlpjklpkmmjmfourxmlqhfg2jxzxbqxhdtwo 291 | 38fivetxvlqfrkhsztlpp2nine 292 | 9hfftvfnggg6vvrmkkfour5ninesrt 293 | rhf9oneninekhkbnmj8one 294 | 6fourthreethreelfqvf 295 | mjlxz7threeszjsbrmbkfourfiveqskmcjzjseven 296 | 119onethree 297 | 72seven 298 | 6817 299 | 4fourfive6eight396 300 | clldsntn4oneseven2cdbknxbcxjszkv 301 | sevennine8 302 | 7fournrzsh 303 | six4cbptcrqrxvbd 304 | 9fourggbtccdmlfrbhgggmdfhfivejddmqvdtwo 305 | 39tpskfourjddhhtglsninefour 306 | gpxqnqhvmpone5bhllnseven 307 | kkjfsmtcjvg6tdfngq 308 | nine4bdvttt5652 309 | 9szdqjl68seven12 310 | poneightone6 311 | 2sh5jmgmqbxsix 312 | kfqkdtrnzfivethreezmbpz1kgddhhqxxq 313 | 6mnkjfszdtdqfive5one 314 | ggqnz9jrpvnszftwodntslxcxcthreetwo 315 | four7two89nine 316 | 132twofour 317 | bghnineqfourjq5 318 | seven58tbqhgl961 319 | threeznrcdmjx8 320 | one692 321 | 9one66grqbdpthhrf19pv 322 | three9threefivefive 323 | 8dqgrchqkdbcnxrzfour 324 | grqvzbsdone119 325 | glhdzhlgnjkkxtwo9eightwosqs 326 | three4five4 327 | 5phtwofive1kghbsthsmntwossztnh 328 | 7nvfsdjgclhone6ninexxlvvhlnvkdhkqgnk 329 | 5nhqd 330 | vcxmllvgr6bnbssblhvhxx 331 | ctgcmpthlqzjrdbgjhhlqsnklbxqgsgckvcbhd46eight 332 | qrqone93mqnfhhxkfivegzv 333 | foursixfivecsvpxqbct7threelrgbxz 334 | qbbcdvcg75mkcnpsrtnxthree3qvbhgrmctk 335 | hcspgtmxczhclq78onenine 336 | 8oneninefourvfthrhthree 337 | 4jmhrthvjxljpvp 338 | three17two 339 | xhtwonepxntwogpqxgfxqkvrh7seven 340 | one28fivesixthreeseventhree 341 | one6two5twoeight 342 | five8nkfhnxlg 343 | vzronefivegnzhjsjftzone8three 344 | 3c4 345 | 8two88thfsth3213 346 | kdpckz7hfzrqs97kdt 347 | 399 348 | tpzv4seven 349 | zxjpsfive4dp19 350 | 491qjcsn9pxkbpf5lkzjmjr 351 | sspbvqtp6oneqnnfive 352 | 96mmbs 353 | sixsrvxrdl8vlqcpfourhjkjk 354 | clggtwo2 355 | 6lrpf4 356 | rhgpskfkbdsxm3jpqs 357 | one8two3 358 | six776 359 | csksfour8five6nine1 360 | 7zlpmzplh4 361 | twofour2flrdhfh 362 | cvrnvjzdgjglfmshmjfknhrbfouronescvcxdvczg66mfhnbgks 363 | two26 364 | 2two4 365 | sgsxnnxxjdhfxhneight31vpjr8sf 366 | nrjkbbdzkkgtxcsdkc5 367 | 9trlgrcbrhhzjkzhrrlgknthreefgmxvceight 368 | lkbmq53sevenninetwo53rx 369 | bxhtwo4fiverxdrone 370 | qxbxndzzrlone941rleightkqfdvdps 371 | 7189fivezx 372 | bqmfctlfr5vfjzkpcbzgoneseventwohmnzgzpthree 373 | krbxccqsmlfour4 374 | threetwoninefbvs1thldjh8 375 | brzpkk2zvbpfzthree4hlhfbtchpl8fivenine 376 | 9jxjflpcpb 377 | 5sxcjjq977zzxjhfcsgthreeeight 378 | 61sevenseven9gbgnh 379 | 36vchhqdqppmcnsjmjjtffour 380 | 27rkbpslsg8d 381 | 57twofivefive56 382 | zq984 383 | xktwo83six7sevenghvsgjnhvjone 384 | nineeightmzhp785hphfive 385 | fourdkkqztwo5sixthreepjlkvgmponefive 386 | two4hjhtsxvpd5t 387 | sevendsjdkpxpqhrjdd43pblvslgtb 388 | 4jzkklthree34tltfvh 389 | 6three85szgdcqjdbnone7 390 | 5seven9vlxgvpsixtdxssnlnsj991 391 | ninefourjlvvnhzkdvpxphxthreeone37sxcfc 392 | 24sevenninenhgbhnine68 393 | tvdfcmjfqmjpsq3zxjlhpb49 394 | qbxgkrb9426 395 | 462threeeight5reight 396 | 15threeeight9rnllzhrhttbsix 397 | qdjlvdvmqsl1jpscsix 398 | 6four579qmfbcvhq 399 | 8m38 400 | 2bgng5shfdzdczbvjtjlxseven3 401 | cgfsgqeightkvkjgpllcnzmfive264 402 | vdrrfnlxpfgfcmxqrcgn1qrgppzxfhtdnhgrvcdvfcnine 403 | 8four45xqqpbjb11eight 404 | 76txvbjngrbmmjssztpchrvxn7 405 | 9hjvbzxsmszc1xninefour 406 | 4fourthreevtxqmhpgxvtxflt 407 | 7threetndgrtbzheight63sd 408 | two8kmrlvxvqlzsixgtbbtpg65fpj 409 | jfkkr5423bbqgbqzdhqrfive 410 | fourvbhsl393 411 | 3kfctvlns264 412 | onejvcljdgxlkqbzxkthreesevenfive4 413 | htgsddp487stjmxcgsix 414 | six3five53fourone 415 | rvkzbqp9lcpqbdnqqjfppv3 416 | onefivethree2tldqjxmrks1nine 417 | blccqs1 418 | three9mtthmnzhlhzk7hrkplfpqq2four 419 | lcrcrxvzqp3eightfour9 420 | mqmvfffour13 421 | 431 422 | 75five 423 | 7zjmdrqmsbz65476 424 | 9gvsix28 425 | kzcqqmfdf8two1gqsevenjrqpvpzh 426 | 47hsckckcxtnbdrbk4ninefive 427 | 8rpshgfxv929vsfmlqsl3six 428 | pnqmreightfmcffive9seven1 429 | 9ninesix 430 | seveneight987 431 | sixnine1cfltxqcthcqptwosdkfl 432 | zktfqvfsevensxmdrthree8xrdkglj 433 | three681t7 434 | fmfr3five1fivedmngqmtrs 435 | tmx38sixksmfzrkrtn 436 | ninenine18 437 | 4sclfsrfive73fivejprvmzhvlp 438 | eight38 439 | xxvgxdnxzk8xdpcjfkqjrrrfn7j 440 | ffrjtvntsix2rhxkkpbtpnkdttbhx1 441 | 516sevenseven9jpdklpfn1 442 | 4dbsfcvgvmlppgs4chzxpvqfqdfvmqvfsmdvz 443 | vtlgqsdj6three5two8k 444 | 65fivelrdtcsixtwoneql 445 | 4twoeight77two 446 | 8nine1r5 447 | six4nz61qxrgr8v4 448 | 3341 449 | kksh2 450 | twofourfourz144 451 | eightfourtfnhmz23vr 452 | seven11qxnhdkczvlfour 453 | nineqdgkxjdjrdh1lcsv 454 | one4sixfszjddf6stmmhfkds1 455 | sixcd39ttsfctrt2xl2 456 | xzbqhlqv6h4 457 | tvtwonepfcssknlqm8two9eighttwo4 458 | vgmpkdvsdnvsjfbtwo6ninesevensdrlbqqrzg 459 | 5788one4seven 460 | five5rccbvbk3ccgnzn34 461 | 9dr35sevennine 462 | 2xmrsvlkfour1tmszk 463 | oneeight4two1scdhclbllljhxkq 464 | 5pffnxjvxcrmdzffm 465 | fivesixjfive7eight2cpjklvqsix 466 | 1gfvcnssixnpqpsh7gjcppxpntwo 467 | xsfbzkkz3zvh 468 | 2184ninesevenfour 469 | 3g7xxzzghflvghnm4 470 | twoonefive87mjsqmqddhvlz3one 471 | two1nxhfgrsixdnone8one 472 | twonrrrldn615kdclqjveight7two 473 | 6eightqmrgrbp63 474 | tmthreetwobnbccjxgpz7 475 | 8ninerseven721v 476 | twoqdjpssix75tonetwothree 477 | 3khjkhbgzdgjqvvnrchqtg63vjt5 478 | hbqlreight18 479 | mkthreefoureightfive2tnccxvltm 480 | threefive195 481 | 7ln 482 | 8fourfoursixrvcthreeldzfxjj 483 | 7rjbdptnllcbdqncsfq 484 | 1ftfffctrz7four4nine7 485 | 3onefourzdtnrmpgpfjtdl3 486 | sixkzkcg43eightwocs 487 | 7fivetwo9nineseven 488 | fivetnqzktdvd72qxpfbftwo 489 | 2fiveqxghjglggxfour55kcfdbdnine 490 | ninegsd6bvpdctspbmmnvdfdqfiveone 491 | nineone1lmkb 492 | lpdqbmsfive6ninenkggzxsf 493 | z7six4ltdsevennine 494 | 2591fbqs7 495 | 1sevenninesix1 496 | 3fbpxseveneight2 497 | sixhtgmvdmxnt85cvdrlftlvtjlv262 498 | foursbqmqfxzvtwoflzxjfphf9nine 499 | twotsszxmtmvvdldqcdkvbtd7one 500 | 1fiveeight 501 | 4threekqgzcnssq5rjt1one33 502 | twotwo8ljtnjnslhldgpfftkqdgdvtpj 503 | eightsevenxqddfsgsevenhgfls4cxsspvvbsjfour 504 | mzrrblkmhq4tkkjgprjjmrlngpfourbzpqmjlbrtgmqgcxdr 505 | 6ctdtqxnfxqpn 506 | 1mkrznchsmdthreeclldtmmnjgdsnineqn6 507 | 53 508 | eighttwoxlsjdxxfive1 509 | 4fourlgszccvnjhmzflflhnsrpm 510 | 6pnfour 511 | 2fivetwo67cjcqseven 512 | fourfivehrkmpcnmg5 513 | 24kjbttvj 514 | fivekjsvjqvrcd4 515 | sixbmphnpgtsvlxpq9three872nmz 516 | 836eight 517 | czbrlmnlcts9three499 518 | 72four 519 | gxsg311 520 | jbfqmr33twofchnkshsjr81 521 | 93snktwo 522 | seven1mcxqjnsqxpngpsix5vjpgqxbmsix 523 | d1srlmseven65 524 | pjnbc6twommxvninebmgqkpsixthree 525 | dgfnrldfdeighttwoxvvpgtzkhftjjh6 526 | sixxgnnvtgbnn6seven 527 | jjd2pjjps9 528 | 8nntpzmlvknl3bsixseven8fbkhvk 529 | hmcj9seven3 530 | gxqfdjgft7four8seven5 531 | csgjjk6xbq4mnhmsix3nlstqqfpxtvfoneightvk 532 | vzeight4rxfggcm17 533 | 9eight2sevenfour3dqcxllvtf 534 | 24npbrhgonefour7 535 | 4two2qcxkvdk 536 | pvgksfone72foureight 537 | 5mkfpmsvmsqfhxnzhjsqrrb8tlb 538 | onetwofour4sjgfnjfq9nrbvtwotwo 539 | hgkszdcrtchgnmkzmhnone3hdlnd 540 | 8fncxdpsix54sixkcbbl9one 541 | vdfrczbnfhtwosrqckt9two 542 | six17tworrlsqh7nineseventhree 543 | g7six 544 | lpl8three65hblm 545 | 89eight1 546 | 2ninegnrqqdgvfsfive 547 | 5five34vbvbdsix3pbbvxxdrc 548 | txkclxnfivexcrgvln27sevenshrrjlf 549 | onefour47two3sevenzsxpq 550 | glhrvvdr2 551 | six4fourpcvngrrndpthree8sixpdzbqcdd 552 | dfgkrkcr4eightthree8one 553 | qxlnf3eight76twofivetwoseven 554 | vvh6fivesix 555 | csppnxxdfg3 556 | four3lkjbcfdzbdcmcsqsdlnnh5 557 | 7two74threedsdr8 558 | rxnrtbjzsixfour6b 559 | kchbtfvbnd3one237fivegjkghkkgd2 560 | two233ltdskthree 561 | 3nine2gkbpnvninegmgqdgr 562 | 3nhpnkl7 563 | threez6ninebghxmffrgkdhjbtwoseven 564 | fourllszfzxmf5threesixthree 565 | 38ldqlmkdkg7cjqjpjdxbkzeight 566 | fivefivethreeone3vjtzzcrz 567 | rsbhdj76three 568 | sjkmgvhhgnsqzhsk4twotwonejf 569 | dcfqtcsccrvnptxrnbjdzgbmsixfourvrftknv95gztj 570 | 8gxgbblnjsd2kzszrhqlxztbxbmsixfczqtzs 571 | vgbzfnccxvdsvgpkthreel26 572 | threeeight3four6sixone6six 573 | threenine1foursixhszppgjbv5 574 | sbkncfseven18qdvtssf17jn4 575 | phgbqgk9lstmfc8hxnfjhnstqbqztthreeeight 576 | gtb5seven 577 | theight2 578 | 3khcnpfdmmbvld8dqckxbrqn1 579 | xvlfsnnbkxxs79rg99zfnzfmdjkpq 580 | xtseven5dtqbrstgl2 581 | eightfjzdpqcfive63rkgxstcspzpthree5 582 | three7qntkfbtjtwothree 583 | three7xgz5 584 | 1gbzzmvpfgg 585 | 3vhdztptqmb 586 | sptwonetwo2 587 | 93eightfnqngzqneightkqvlzrfour7 588 | 15fivethreefourqpblvgzncb5seven 589 | threethreefivenpsix53hnbv 590 | 75fivenrsfmgzz26mtmzg4one 591 | 9seven6631zrrjhx3 592 | 1eightp1kxvmg 593 | tpkjbzglxhffpnftxz2h3nxxnzrnjrnine5four 594 | fivefivetbfnbvqsmmcfsqq69 595 | eightgvttxgjnmrpxvctthreetwo6jfrggjmrm 596 | twoone1threezdpmqthxf17oneightcj 597 | 8oneone2fghmrrrpxdbmjqgnine3seven 598 | tjrjmtqlpone6oneqdmbchplflcxvlone 599 | nbck42sevenhtqvjzctmqzpnnqtjpmj 600 | foureighttwoseven525 601 | ninehhqszsixthreebbjdvkhxjdzthfzjqvx6nine 602 | onetnsckstxvlfour5onenine 603 | four5qskksqdvpl 604 | 5nxpxjtfp 605 | bbeightwo8 606 | ninerdcninesix2four 607 | 35kfivethreebgcpnslqh75two 608 | one8two 609 | 4f8three 610 | seven6hmrqhbz72eight3 611 | nine5dphq 612 | clhcmnkkdjsixfour8fivethreebdrrgbs 613 | 6fivesixxvnzdz1eight99 614 | fivesevenbeightsix8 615 | njzgflpxtr3drdkjqddtcsix 616 | 6fivedpq 617 | zhdpsdpvpnvmxbh7 618 | tjx13vfsevenlqftrtthszmcfnl 619 | 6foursevenk8sixeight 620 | seven9k 621 | nxkxfcjbp7five78 622 | 79twofzttrkpbrgsixvpxbr 623 | gcvone98 624 | rd31threeeightnine 625 | jmveightwo9five 626 | eightfive94four576 627 | sevenz5oneightps 628 | ninezfsshxpl6rqpjdlzvfourlpqxb5rfprmvfrlsix 629 | nbxmtcs2 630 | eightsltgcjshndmtj2nine 631 | dfdtwonefour5six3z8gjdx7 632 | eighttwofive2 633 | tgj29nine5mzhckzhkrzfours 634 | 5onebjtfourqtpnccz 635 | 3lsevenonebpgfgonethreeeightwos 636 | 99lqdd 637 | kslzrndhk5sevennine1sixlsfour5 638 | bnndeights741 639 | gbrkxx4six2eight 640 | 9twoonenzdqrs 641 | shcbsvtstq8threesix9 642 | eightcjss8 643 | fivenine4qqnsqctcrkdhlm 644 | q6ninejsrmsixghvzjqghgkskqqgdgmgzplr 645 | 1sixs 646 | ldqzccvqhznfrbthreeseven516gdzxb 647 | sixsrmspzp4ftrrv5three 648 | 71onetwo 649 | 9196hftrxbj2 650 | 4jqszj91ninegvzrsctzl 651 | 5threeeightfivexzfqfvthreendsqnfseven 652 | nxvfzcqlxg886 653 | 6sevendsnsz 654 | nine79244onesevenone 655 | fiverhfvgn2sevenlcthqvfive 656 | dzsnvjzv326vtnmg 657 | lzbnsk3622fivenls 658 | sevenfive8zghpnineeightqxsjeightthree 659 | sevengxxxxgcdblnprdvpp8pctwofive 660 | gzbsp3rjfbjblktwohqslf 661 | lbfqczsrfiveghzgkdksqggkrjvtpczlrhtrms9 662 | one5brpbthbtrsix3 663 | sixgcqss52sjkm 664 | sixkbpmsjrdt86 665 | 978 666 | cmvhrjtnl73hhlcq6 667 | two23ccxsnhfszxvcncv99 668 | eight4threeoneqtjksevenxzkqseven1 669 | 89zzq 670 | dxtnzqprbxtcqdnnjjjzslpmxxxjzhlhzzdk18seven 671 | 84sevensvkqxjssvrsrsevenvztvchnine 672 | 7psfkscvgtwo4grpfhkbzxfive 673 | kdpjpbcgjcldrfkrnrone3 674 | 77ninendlqsp 675 | 5nine7 676 | six7mlhxjsevensevensix9 677 | qbnggfourtwohh1 678 | veightwohdfdqzbkfclkxjxqjgjqjseven4 679 | qlfourqtbj94vdlnb16 680 | dgmhlxqhz8ninetwo42 681 | nine45txjpkmhpccdkpmhqlzkvcvgmhs 682 | fourtwo6twonlrnt4 683 | 4qjkkl 684 | 6sdlcshk1five6onefive3 685 | five7z 686 | 5ninedszgbvnbmsggqxxbsxkzptnfxfh1vbhgnnsd 687 | 7mcdfjvthfivesix27 688 | four8six 689 | 5ptvzjjlkbttwothreedbvc 690 | 8threefivehlggnpccck5 691 | hvzrkrvvheight37 692 | 2kqzgx2fxvqlfour28eight 693 | 9lxlntmdksmfivesix4 694 | cjrone3four 695 | 9qstctr8 696 | twojrnfgrgtbldrztnd5four 697 | 54ninetwofhvgjntvxdvhszpfklbbxrppmtsrhdeightwodbb 698 | fournine2three2twomxlngklgs 699 | eightninefour9four16vhrvzcnhrf 700 | 66mlkjff25jplqskbrvmdhlsz 701 | jrjnbqqzbqfivextmcjplnxb1twoeighttsh 702 | two9ssxpjb887 703 | crdzssp9 704 | two7kdxhhrxd8eight 705 | ninefbhhjxgcveighteighttwofive6 706 | ptqmngxdzd2 707 | pfcdvxjnvhfour1sixg 708 | eight1chshckfpnxdzf1cr 709 | bdbgr822mjsln2qvztrsnglxntdq 710 | six84j1bjtshnfmc 711 | seven4nineeight175ninenine 712 | 968sbdfxkmrlcxj6 713 | 854seven181lbrcv 714 | 5sixcr9 715 | 4fourdzfournine81 716 | twotwo686zqhkveighttwo8 717 | eight992lklcjtwothgl 718 | 8eightfourxjjdtbeightpthtnhpb 719 | 5vponeeightlsbxthfpzjseven 720 | fffbpvmkvntkhrdgxqfxjdglsqztd5 721 | three6f 722 | nine5163jbhpzbvjcg8 723 | bzkfgtqnmonetpjjtbc5nvtbndtdbx 724 | ndtsevenone3ndtljbnsixqxntsdslnnine 725 | 8ffour283twonft 726 | tzdrstwo2jvnqvtjfsjzsldlfx3 727 | srxzlxgjpprcmt4mcljsix6eight 728 | fnm3oneightsdn 729 | fourgxsixthreenine1three1hcf 730 | sevenonesixnine78onerdone 731 | three3threexmtbzcjzrd 732 | mm6rvmbfvsgeighthzjkjnnqpsm 733 | fourfourtlqn1nine2 734 | 3hg 735 | 7smbmnrhpk 736 | mmxkkdztxklhhfivetwo9 737 | sixfive36jvtnsrscchgkg 738 | 94fr5threesixone 739 | xf5threes 740 | xskoneightsixclzkkthree36three 741 | twocdjjccb7sixpcnz 742 | fjlqpshqpcdsixfive8mbjj 743 | x2nine33 744 | threevgxzlhdpnxpgrsp4tgp2lfive 745 | kmssbpb9fhzqhgqlsm2threethree 746 | sixcvfmgfx8xhfncrcqmptwovkqxjxddkdfnnkl 747 | foursevenl5sevenjlljthmqqp4 748 | nine249rhqxfmxktwofourdtqrpckbhtthree 749 | onetwo5871eight 750 | 7dxrrnqrxgjxkvlzffourlgtqngmdlcfourllstsr 751 | eight8four7kfcqcvdffzlzsix 752 | qnvgz5jvvtpvnvfneight78 753 | five9six 754 | 2jztl6lnxmcqsb95six 755 | qx1three7npjkznmhbp3seven 756 | ffive1onefour 757 | 5lnhjfoureight 758 | 5573htvcvhkhb 759 | 998zfcsninejnjcgnkd 760 | five4kdcncpsevenjftzlxmqlzxlfour8 761 | pkxfxvhlsix5seven 762 | 3sevenzxz 763 | 4sixninesgctmxxxx 764 | 26lrlphfive4pcpdsnlntjsixtlgjrddmkx 765 | hp49three48rkxmzv 766 | rjrllvt2twotwo16four4 767 | seven77dccfgqbf1 768 | qzxgztxgsbqjshthree7sevenhvsczkqllqoneone3 769 | eightqlnnine3 770 | hvtmzrcxx57gghsktwoqvxdzqfoureight 771 | 7eight2qmvdg8fdrxfzbtcrsgmhl2lfqpx 772 | bkrbmrhvfsvm4 773 | ninenine6six 774 | gzkmgrvqckrgtjchgfsixninefvffrzzln4mphqtwo 775 | crprs2 776 | 54tgzbrbqcljlfchpdt 777 | rm27two2jfpccv 778 | 3447two3ltkbr 779 | 9twothreeone4 780 | llndzmsbpsevenfivebbprclghvfrpm3 781 | nine7kglqppgccvgbbpffqhlkk 782 | 5qkxvnchcjx8three1 783 | rkfmd5rnine 784 | sevenonehzrgeight82eight 785 | eight63lbkjztkcjhtdt1bseven 786 | 7kcrzxddfsixfive28one9 787 | sxbmvvprh1hzcjhfg 788 | sixrtmzrcxb9 789 | 9sevenhxkvrjz7seven8 790 | eight3nine 791 | 1eight24seven7kctpgv 792 | 4437 793 | 8skmrkxc2 794 | six92onesix 795 | eighttngjnngzvvfive473five 796 | oneseven4 797 | 56bsrhvjtvnine 798 | 4two4seventwo4lnj2 799 | 8mmpvxdqjnine2sevensevenf8eight 800 | eightnineone2six 801 | 4fourn 802 | 87six 803 | h6nine 804 | sixnnkxpxtwo394mtwo 805 | twobgqqjchb2hpjsvv 806 | snnbnf6fqtprsixmszrpvxzbxsevennmzxfive 807 | three3eightqxkbvxftj358 808 | fivefivefourfour8four3 809 | fivejmdpfourbgsdkmvqmf3xnbnm72 810 | 9twosixthree12xgtcqx8seven 811 | 3jnrjlbfdk4blmpfzrpp2ninenine 812 | 3321fivesix 813 | 1six4nnfvrxhz5kgqththreesix 814 | snkszmprd2cpjrvbhlcpfqrr18oneonebkrvcnk 815 | 9fgj21nine1dtfdgm 816 | jh6tc7xz 817 | 3one9 818 | 8oneqtsgxrsbsnfjlfivenine 819 | 5bcktzbvnjsvzmrlpr2dzhgqvgnine9 820 | 4ghnqz 821 | 7threeoneeightsevenjzqnlpsmlnfourgp1 822 | xfgxnvdxj1 823 | 18ftwogtmbdgmc 824 | eight9gbnptk 825 | 697threefive7three6 826 | 96h6bmbttcmpm4 827 | cmgtwo3 828 | 6sevenxxpxzbbsdg36oneonezptwo 829 | 8fivedzpdqklxh54sljvfr92 830 | vg6rsevenjvrc77 831 | vqm55hdkzbfqsn 832 | 8eight6 833 | nine4eight6fourthreezmnf7 834 | bxlsntdmthreeninetwomzjhcdzxbktwodzrlzkcc2qjn 835 | kztghlsgkfpkkpstfgjsxzvczpqxzchmd9 836 | five1krfeight7 837 | fourjh5mcmpxgkfrb9 838 | xrrh3xfgsflnh3 839 | csqpfhz9 840 | 2nine1jxlrjx 841 | sixeight1gcpmqtvcp3five1fzpclbtbpn 842 | 9twosix6zjnpskrtwofour 843 | eightnr9three3sevensix 844 | vtwoneone7ninefive 845 | sixdcmmtcgkmlbqlbmdcnztpseven7tbsrrbntnkeightfive 846 | xcjxjxlshj3four 847 | xbccvqgmt8eighteightninemgst 848 | fourfive938ninehkgsjxb 849 | zdoneightfive8 850 | four6five 851 | 2fivefncninethree3kdc1rg 852 | threef2threefivezfnnn 853 | cmtsnssneightthree4 854 | nine64rfnvff4krtrqskx2 855 | 414dzfxfkqkf9onefourmdxh 856 | ninenbdkxcbjlsix7 857 | 69hqfour2eightsix 858 | vkpcznjlktxthreenine5zgtxknjpcone 859 | six4nzvnplntzthbk 860 | 799 861 | poneightnthnsmkrsixgqvmzoneninetwo7 862 | 9seven3eight 863 | seven834eightvqdrgkxnfsdqbnfgxzvg 864 | qgxlnprzl4six 865 | fourtwozdflbcvkkp3four2qnmxvdqz 866 | 8ndrb45 867 | five2gnrgdxbspqlplxtbcnn 868 | drnrzgjsrq1 869 | onensdmddvsd2skbrtwosevenfzvkpnh 870 | 4mrbkjvdz 871 | nphlsgmhmmzkmx79 872 | hknkxl83twossqxcbpd 873 | 7nqjkmmninekmhsix1jqq5 874 | zvnmvfivesixeighttvpqnjzjsnbqdsj5foureight 875 | fivepr6 876 | one5mcbonexqbhnqtcrcsix 877 | 9fourbmbhfkoneone 878 | 78khztltsevensix 879 | 1jmnpnjtxhcthreec924 880 | xptwo1 881 | 6twogblptvrxfourxmtffqcbhx27six 882 | kbksdcgnseventwofphftkxcjlzdld4seven 883 | threeninevmqjrp668bjqsixnine 884 | hrllthree2onenmnrbljfxssmthree1 885 | 3mxm 886 | four1fp7d78nine 887 | 3eight1five59nine7 888 | eightmqhp79two7eight 889 | dkvjxhxl8twoeightwog 890 | sevennzszzpcvqscttx92njhpgmhfctseven 891 | six68 892 | rqhjlt88sixtqscbgx 893 | 8fivethree2dxjczsqeightone 894 | fsqlrhprtbgsdz3ftrjvtqnjnine 895 | 64onevbtdmq5cssxhvd434 896 | qllvxktqzkmchqthreektnpbtjnd6lfsthvrnq 897 | 7ctpnhpslzzcdtpnine8one7three 898 | 2one5581 899 | 1six2twonecrb 900 | 245ninefive8 901 | 9eight335 902 | 1eightfive 903 | dqljgrtgvbsix1gxjbptbhspknrzninesixone 904 | seven9dcthree 905 | 8eight3plkczntneightq95ll 906 | fnnvpn9five2onexbvmjcfour18 907 | twoseventwosjmnnx5four 908 | 2dlpfdd 909 | lxdxfngzsixseven47qcf 910 | 3ctpdv8prmhmpjbbmlsjrdcrb4nlfmklpxj9 911 | nine4ghsbstrhkp 912 | hgmggcrfmqbjrsjvhm142 913 | lzkj2mqbfdvcxqbthree 914 | 1xeightpcht7mn 915 | 94vgrkqdbcsixninefour95x 916 | foursevenvzmfd5bqddnn3twoxrcnhqsm 917 | bfcrq4lfsbbjqrmseven 918 | hcntfdqcnxqfvjvl7jfppmbltwo 919 | four35cmvsixfivenineqgt8 920 | ninefive1mpzdcrhmkzhqpdmjcb 921 | 8qtmxncgvr73 922 | bktrlrqvvz68clbz2seven6four 923 | nsqhqgxjcsixzldhvcnnfivenqz2 924 | 8eight5np 925 | 6grqzz6sevenonep 926 | 2fivenbcpmrhxfour15 927 | 2hhfhjjzgxz787 928 | xdzf595 929 | nine8qslr 930 | n241nxdkskzsevenone 931 | 25ksx49lrcroneightz 932 | 9fourone41sevenonefive9 933 | 8fiveninefivenine58 934 | nine92pzm 935 | cbsdbjxmjkjld62fdqcrthreegdnlhhbfc 936 | 1mtpthreebshnseven 937 | 1mbkhbjbsmpc9three5 938 | 8lqxq27mfkgvkblrlpgvnine 939 | lzb21vmchthree7sevenfour 940 | 5hxseven7jhsqlsftml 941 | tsmvj58zone49 942 | kkmlxnnn4nlbvxsbnlhkjvv7 943 | 5lprvntnlonendrjqthree6rrhl 944 | htlpb9 945 | fxqgpzgktwojmnlsix4rhffkxcrgv5 946 | dxdddnprdnvjscqsmqsixsixfive4 947 | 3threeeightdtwo1967 948 | 5clzsqqhn6 949 | gfkqsfour747 950 | dgfdskvk6 951 | 6four7six5threeqgvjdg6eight 952 | 8eighteight 953 | 28456eightwojbg 954 | two8l 955 | lqzthfhzfour4qsggmhhrcbldhhpq 956 | rlvrxninethree4 957 | n6sevensixtwo 958 | 2s5sixxfhhnmpjtseven 959 | three9three 960 | eight91dgfnqjffkdxcmljvfrmh4 961 | twothree5 962 | 6sevens7 963 | 8six9mnksevenkm2fdhhqvfhscqs 964 | twotwoeightsixone24 965 | rhqnssh2nine 966 | dhdtxxcndninethree9 967 | hvvfzxfivemb3 968 | dgbgglhmtnkrpq4four51dlpcfivefour 969 | 4qcqhrshncqseven9kvq6hmghcbfl 970 | x3qvkxmh3m7 971 | cqqlrjkjbrggmccctbjzcjqktfngmmkftddponemqj17 972 | fivesixfoursksxqmqrmseven5 973 | fivethreepxnxtgpgmrtwo1 974 | 5threenine 975 | 9five4plblgvnfcfoursixmsgfive 976 | cqjsfxvcfdseightfour9g857 977 | fourthree7onegpmlrrkmtj881 978 | gjdvx1fourseven 979 | lcvhmlzplhgvmnhcgthree8xdtvrck9 980 | four26 981 | qnvrspvpvqsv3xfbzrrjjnsevenvvdzknkqcmbpdlg 982 | 22611zfive 983 | ninegbsscnbtcnzhsevenfmfvmv3lrbthree3 984 | two76 985 | xhn19qninehqtbvrsdfour 986 | fourk4cqtggvjseven 987 | threecflx6xllnd 988 | dknmdpcqfivelvppbtone3 989 | 6dnzxninebdldgsphsppzjzpgbklbbf9gcdc 990 | 587cdbcb2mspbgbl 991 | twojkblghsctseven8eight 992 | 2xmdmtgcjhd8eighttwo 993 | nine6qpfzxhsdsfour9 994 | 9rvqhjvmh7kcvnineccn9rzpzs 995 | tbsxkhhv6twozrtczg6seven 996 | ccpeightbcvknglvcv81gcjnlnfnine9 997 | 4twoscpht 998 | qdgdrtx9onefourdcvctldjnpcdjbc 999 | cjxkxsgmql4xxgjtpdcbmsixeight 1000 | 739 -------------------------------------------------------------------------------- /elf.pl: -------------------------------------------------------------------------------- 1 | :- module(elf, [run/1, run_codes/2, run_string/2, run_string_pretty/2, repl/0]). 2 | :- use_module(library(dcg/basics)). 3 | :- use_module(library(dcg/high_order)). 4 | :- use_module(library(yall)). 5 | :- use_module(library(readutil)). 6 | :- use_module(elf_fmt). 7 | :- use_module(elf_record). 8 | :- use_module(elf_map). 9 | :- use_module(elf_file). 10 | :- use_module(elf_ref). 11 | :- set_prolog_flag(double_quotes, codes). 12 | :- set_prolog_flag(elf_use_fetch, false). 13 | :- set_prolog_flag(stack_limit, 2_147_483_648). 14 | 15 | %:- det(method/4). 16 | 17 | version('0.3'). 18 | 19 | % Reserved words that cannot be names. 20 | reserved(and). 21 | reserved(or). 22 | reserved('_'). 23 | 24 | % Print error and fail 25 | err(Fmt, Args) :- format(string(Err), Fmt, Args), writeln(user_error, Err), throw(err(Fmt,Args)). 26 | 27 | ws --> "#", string_without("\n", _), ws. 28 | ws --> [W], { code_type(W, space) }, ws. 29 | ws --> []. 30 | 31 | % At least one whitespace 32 | ws1 --> [W], { !, code_type(W, space) }, ws. 33 | ws1 --> ws, [W], { !, code_type(W, space) }. 34 | 35 | %% toplevel 36 | %% statements (list of expressions) 37 | %% assignment ": " 38 | %% parenthes "(" ")" 39 | 40 | value(val(N)) --> number(N). 41 | value(val(S)) --> "\"", string_without("\"", S), "\"". 42 | value(val(Ch)) --> "@", [Ch]. 43 | value(val(true)) --> "true". 44 | value(val(false)) --> "false". 45 | value(val(nil)) --> "nil". 46 | value(val(regex(R))) --> "`", string_without("`", S), "`", { string_codes(Str, S), re_compile(Str, R, []) }. 47 | 48 | symbol_chf(Ch) --> [Ch], { code_type(Ch, alpha) }. 49 | symbol_chf(95) --> [95]. % _ 50 | symbol_ch(Ch) --> [Ch], { symbol_ch(Ch) }. 51 | symbol_ch(Ch) :- code_type(Ch, alnum). 52 | symbol_ch(95). % _ 53 | symbol_ch(45). % - 54 | symbol_ch(63). % ? 55 | 56 | symbol_(Name) --> symbol_chf(Ch), sequence(symbol_ch, Chs), 57 | { atom_codes(Name, [Ch|Chs]) }. 58 | 59 | symbol(sym(Name)) --> symbol_(Name), { \+ reserved(Name) }. 60 | arg_(arg(1)) --> "$". 61 | arg_(arg(N)) --> "$", integer(N). 62 | prev(prevval) --> "_". 63 | ref(R) --> symbol(R); arg_(R); prev(R). 64 | 65 | mref(mref(Name)) --> "&", csym(Name). 66 | mref(mref(Op)) --> "&", op_(Op). 67 | 68 | mcall(mcall(Name, Args)) --> symbol_(Name), "(", ws, mcall_args(Args), ")". 69 | assign(assign(Name, Expr)) --> symbol_(Name), ":", ws, exprt(Expr). 70 | assign(assign(Name1, [Name2], Expr)) --> symbol_(Name1), ".", symbol_(Name2), ":", 71 | ws, exprt(Expr). 72 | 73 | mcall_args([A|Args]) --> exprt(A), ws, more_mcall_args(Args). 74 | more_mcall_args([]) --> []. 75 | more_mcall_args(Args) --> ",", ws, mcall_args(Args). 76 | record_def(record_def(Name,Fields,Methods)) --> symbol_(Name), "{", record_def_fields(Fields), record_def_methods(Methods), "}". 77 | record_def_fields([]) --> ws. 78 | record_def_fields([F|Fields]) --> ws, symbol_(F), more_record_def_fields(Fields). 79 | more_record_def_fields([]) --> ws. 80 | more_record_def_fields(Fields) --> ws, ",", record_def_fields(Fields). 81 | 82 | record_def_methods([]) --> ws. 83 | 84 | record_(record(Name, Fields)) --> symbol_(Name), "{", record_fields(Fields), "}". 85 | record_fields([]) --> ws. 86 | record_fields([F-V|Fields]) --> ws, symbol_(F), ":", ws, exprt(V), more_record_fields(Fields). 87 | more_record_fields([]) --> ws. 88 | more_record_fields(Fields) --> ws, ",", record_fields(Fields). 89 | 90 | map_new(map_new(Fields)) --> "%{", map_fields(Fields), "}". 91 | map_fields([]) --> ws. 92 | map_fields([F-V|Fields]) --> ws, exprt(F), ":", ws, exprt(V), more_map_fields(Fields). 93 | more_map_fields([]) --> ws. 94 | more_map_fields(Fields) --> ws, ",", map_fields(Fields). 95 | 96 | 97 | fun(fun(ArgNames, Statements)) --> 98 | "{", optional(fun_argnames(ArgNames), {ArgNames=[]}), 99 | statements(Statements), "}". 100 | 101 | % Argument names: comma separated symbols ending with | 102 | fun_argnames(ArgNames) --> sequence(ws, symbol_, (ws, ",", ws), (ws, "|"), ArgNames). 103 | 104 | lst(list(Items)) --> "[", list_items(Items), ws, "]". 105 | list_items([]) --> []. 106 | list_items([I|Items]) --> ws, exprt(I), more_list_items(Items). 107 | more_list_items([]) --> []. 108 | more_list_items(Items) --> ws, ",", list_items(Items). 109 | 110 | op_(op(*)) --> "*". 111 | op_(op(/)) --> "/". 112 | op_(op(+)) --> "+". 113 | op_(op(-)) --> "-". 114 | op_(op(>)) --> ">". 115 | op_(op(>=)) --> ">=". 116 | op_(op(<)) --> "<". 117 | op_(op(<=)) --> "<=". 118 | op_(op(=)) --> "=". 119 | op_(op('%')) --> "%". 120 | op_(op('++')) --> "++". % append lists 121 | op_(op(and)) --> "and". 122 | op_(op(or)) --> "or". 123 | 124 | % check end, but don't consume it 125 | end(End), [Ch] --> [Ch], { memberchk(Ch, End) }. 126 | end(_) --> ws, eos. % whitespace at the end also closes statements 127 | 128 | expr(A) --> value(A); symbol(A); arg_(A); lst(A); mref(A); prev(A); fun(A); op_(A); mcall(A); record_def(A); record_(A); map_new(A). 129 | expr(sub(S)) --> "(", exprt(S), ")". 130 | 131 | exprs([]) --> []. 132 | exprs([E|Exprs]) --> 133 | expr(E), more_exprs(Exprs). 134 | more_exprs([]) --> ws. 135 | more_exprs(Exprs) --> ws1, exprs(Exprs). 136 | 137 | % Take expression parts and turn it into execution tree 138 | exprt(E) --> ws, exprs(Es), { exprs_tree(Es, E) }. 139 | exprs_tree(Lst, op(Left, Op, Right)) :- 140 | % Split by binary ops 141 | once(append([Before, [op(Op)], After], Lst)), 142 | exprs_tree(Before, Left), 143 | exprs_tree(After, Right). 144 | exprs_tree(Lst, Lst) :- \+ memberchk(op(_), Lst). 145 | 146 | stmt(St) --> exprt(St). 147 | stmt(St) --> assign(St). 148 | 149 | statements([Expr|Stmts]) --> 150 | ws, stmt(Expr), 151 | more_statements(Stmts). 152 | more_statements([]) --> ws; eos. 153 | more_statements(Stmts) --> ",", statements(Stmts). 154 | 155 | 156 | %% Walk statements, find items 157 | walk([First|_], Item) :- walk(First, Item). 158 | walk([_|Items], Item) :- walk(Items, Item). 159 | walk(stmt(Target,_Methods), Target). 160 | walk(stmt(_, Methods), Item) :- walk(Methods, Item). 161 | walk(method(Item,_Args), Item). 162 | walk(method(_,Args), Item) :- walk(Args, Item). 163 | walk(fun(_, Statements), Item) :- walk(Statements, Item). 164 | walk(op(Left,_Op,Right), Item) :- walk(Left,Item); walk(Right,Item). 165 | walk(Item, Item). 166 | 167 | 168 | 169 | % Functions, method references and regexes are callable 170 | is_callable(fun(_,_)). 171 | is_callable(mref(_)). 172 | is_callable(regex(_)). 173 | 174 | %%%% Evaluator 175 | 176 | % The evaluation state is a compound term 177 | % 178 | % ctx(env{foo: 42}, Args, Prev). 179 | % Where env is a dictionary containing lexical bindings 180 | % and Args is the current list of arguments to fn call 181 | % Prev is the previous result referred to by _ 182 | 183 | builtin_class('List'). 184 | builtin_class('Number'). 185 | builtin_class('Nil'). 186 | builtin_class('Boolean'). 187 | 188 | class(rec(C,_), C) :- !. 189 | class(nil, 'Nil') :- !. 190 | class(false, 'Boolean') :- !. 191 | class(true, 'Boolean') :- !. 192 | class(X, 'List') :- is_list(X), !. 193 | class(X, 'Number') :- number(X), !. 194 | 195 | 196 | % DCG state 197 | state(S), [S] --> [S]. 198 | state(S0, S), [S] --> [S0]. 199 | 200 | push_env, [S, S] --> [S]. 201 | pop_env, [CtxSaved] --> [ctx(_,_,_), CtxSaved]. 202 | setprev(P) --> state(ctx(E,A,_), ctx(E,A,P)). 203 | setargs(A) --> state(ctx(E,_,P), ctx(E,A,P)). 204 | setenv(Name,Val) --> state(ctx(Env0,A,P), ctx(Env1,A,P)), 205 | { put_dict(Name, Env0, Val, Env1) }. 206 | getenv(Name,record_ref(Name)) --> [], { once(record_field(Name,_,_)), ! }. 207 | getenv(Name,Val) --> state(ctx(Env,_,_)), 208 | { (get_dict(Name, Env, Val), !); err('Name error: ~w', [Name]) }. 209 | 210 | eval([Expr|Methods], Out) --> eval(Expr, Val), eval_methods(Methods, Val, Out). 211 | eval(sub(Expr), Out) --> eval(Expr, Out). 212 | eval(sym(Sym), Val) --> getenv(Sym,Val). 213 | eval(assign(Name, Expr), Val) --> 214 | eval(Expr, Val), 215 | state(ctx(Env0,A,P), ctx(Env1,A,P)), 216 | { put_dict(Name, Env0, Val, Env1) }. 217 | 218 | % Assign new method to record 219 | eval(assign(At, [Path], Expr), Val) --> 220 | { once((record_field(At,_,_);builtin_class(At))) }, 221 | eval(Expr, Val), 222 | { asserta(record_method(Path, At, Val)) }. 223 | 224 | eval(val(V), V) --> []. 225 | eval(list([]), []) --> []. 226 | eval(list([H|T]), [Hv|Tvs]) --> 227 | eval(H, Hv), 228 | eval(list(T), Tvs). 229 | eval(mref(Name), mref(Name)) --> []. 230 | eval(prevval, Val) --> state(ctx(_,_,Val)). 231 | eval(op(Left,Op,Right), Val) --> 232 | eval(Left, Lv), 233 | eval(Right, Rv), 234 | { once(eval_op(Op,Lv,Rv, Val)) }. 235 | eval(fun(A,S), fun(A,S)) --> []. 236 | eval(arg(N), V) --> state(ctx(_,Args,_)), { nth1(N, Args, V) }. 237 | 238 | eval(record_def(Name, Fields, _Methods), record_ref(Name)) --> 239 | [], 240 | { length(Fields, Len), 241 | findall(I, between(1,Len,I), Is), 242 | maplist({Name,Fields}/[I]>>(nth1(I, Fields, F), 243 | asserta(record_field(Name, I, F))), Is) }. 244 | 245 | eval(record(Name, Fields), R1) --> 246 | { record_new(Name, R0) }, 247 | eval_record_fields(Fields, Name, R0, R1). 248 | 249 | eval(map_new(Fields), Map) --> 250 | { map_new(Map0) }, 251 | eval_map_fields(Fields, Map0, Map). 252 | 253 | eval_map_fields([], M, M) --> []. 254 | eval_map_fields([KeyExpr-ValExpr|Fields], Map0, MapOut) --> 255 | eval(KeyExpr, Key), 256 | eval(ValExpr, Val), 257 | { map_put(Map0, Key, Val, Map1) }, 258 | eval_map_fields(Fields, Map1, MapOut). 259 | 260 | eval_record_fields([], _, R, R) --> []. 261 | eval_record_fields([Name-ValExpr|Fields], Record, R0, R) --> 262 | eval(ValExpr, Val), 263 | { record_set(R0, Name, Val, R1) }, 264 | eval_record_fields(Fields, Record, R1, R). 265 | 266 | lift('++') :- !, fail. 267 | lift('=') :- !, fail. 268 | lift(_). 269 | 270 | eval_op_rl(Op, R, L, V) :- eval_op(Op, L, R, V). 271 | eval_op(Op, L, R, V) :- lift(Op), is_list(L), \+ is_list(R), maplist(eval_op_rl(Op,R), L, V), !. 272 | eval_op(Op, L, R, V) :- lift(Op), is_list(L), is_list(R), maplist(eval_op(Op), L, R, V), !. 273 | eval_op(+,L,R,V) :- V is L + R, !. 274 | eval_op(-,L,R,V) :- V is L - R, !. 275 | eval_op(*,L,R,V) :- V is L * R, !. 276 | eval_op(/,L,R,V) :- V is L / R, !. 277 | eval_op(>,L,R,false) :- L =< R, !. 278 | eval_op(>,L,R,true) :- L > R, !. 279 | eval_op(<,L,R,false) :- L >= R, !. 280 | eval_op(<,L,R,true) :- L < R, !. 281 | eval_op(<=,L,R,true) :- L =< R, !. 282 | eval_op(<=,L,R,false) :- L > R, !. 283 | eval_op(>=,L,R,true) :- L >= R, !. 284 | eval_op(>=,L,R,false) :- L < R, !. 285 | eval_op('%',L,R,V) :- V is L mod R, !. 286 | eval_op('=',X,X,true) :- !. 287 | eval_op('=',_,_,false) :- !. 288 | eval_op('++',L,R,Append) :- is_list(L), is_list(R), append(L,R,Append), !. 289 | % nil acts as empty list for append 290 | eval_op('++',nil,R,R) :- !. 291 | eval_op('++',L,nil,L) :- !. 292 | eval_op(and,true,true,true) :- !. 293 | eval_op(and,false,false,false) :- !. 294 | eval_op(and,true,false,false) :- !. 295 | eval_op(and,false,true,false) :- !. 296 | eval_op(and,L,R, true) :- \+ falsy(L), \+ falsy(R), !. 297 | eval_op(and,L,_, false) :- falsy(L), !. 298 | eval_op(and,_,R, false) :- falsy(R), !. 299 | eval_op(or,true,true,true) :- !. 300 | eval_op(or,true,false,true) :- !. 301 | eval_op(or,false,true,true) :- !. 302 | eval_op(or,false,false,false) :- !. 303 | 304 | eval_methods([], In, In) --> []. 305 | eval_methods([M|Methods], In , Out) --> 306 | eval_method(M, In, Intermediate), 307 | { ! }, % commit to this method call result (don't leave choicepoints around) 308 | eval_methods(Methods, Intermediate, Out). 309 | 310 | eval_method(mcall(Name, Args), In, Out) --> 311 | eval_all(Args, ArgValues), 312 | method(Name, In, ArgValues, Out), { ! }. 313 | eval_method(sym(Name), In, Out) --> method(Name, In, [], Out), { ! }. 314 | eval_method(mcall(Name,Args), _In, _Out) --> 315 | { length(Args,Arity), err('Method call failed: ~w/~w', [Name, Arity]) }. 316 | eval_method(sym(Name), _In, _Out) --> 317 | { err('Method call failed: ~w/0', [Name]) }. 318 | 319 | eval_all([],[]) --> []. 320 | eval_all([In|Ins], [Out|Outs]) --> eval(In, Out), eval_all(Ins,Outs). 321 | 322 | eval_call(fun(ArgNames, Stmts), Args, Result) --> 323 | push_env, 324 | setargs(Args), 325 | bind_args(ArgNames, Args), 326 | eval_stmts(Stmts, nil, Result), 327 | pop_env. 328 | 329 | eval_call(mref(op(Op)), [[Left|Right]], Result) --> 330 | [], 331 | { eval_op(Op,Left,Right,Result) }. 332 | 333 | eval_call(mref(Name), [Me|Args], Result) --> 334 | { atom(Name) }, 335 | method(Name, Me, Args, Result). 336 | 337 | eval_call(regex(R), [Lst], Result) --> 338 | [], 339 | { string_codes(Str, Lst), 340 | re_matchsub(R, Str, Match) 341 | -> (dict_pairs(Match, re_match, Vals), 342 | sort(Vals, ValsSorted), 343 | maplist([_-S,L]>>string_codes(S,L), ValsSorted, Result)) 344 | ; Result=nil }. 345 | 346 | bind_args([], _) --> []. % extra arguments, might be $ references, don't care 347 | bind_args([A|Args], []) --> [], { err('Too few arguments provided, missing: ~w', [[A|Args]]) }. 348 | bind_args([N|Names],[V|Values]) --> setenv(N, V), bind_args(Names,Values). 349 | 350 | % Eval a method defined on a record 351 | eval_call_my(fun(ArgNames, Stmts), My, Args, Result) --> 352 | push_env, 353 | setargs(Args), 354 | bind_args(ArgNames, Args), 355 | setenv(my, My), 356 | eval_stmts(Stmts, nil, Result), 357 | pop_env. 358 | 359 | eval_stmts([],Result,Result) --> []. 360 | eval_stmts([Stmt|Stmts], Prev, Result) --> 361 | setprev(Prev), 362 | eval(Stmt, Intermediate), 363 | eval_stmts(Stmts, Intermediate, Result). 364 | 365 | eval_if(_, Then, Then) --> [], { \+ is_callable(Then), ! }. 366 | eval_if(Bool, Then, Result) --> { is_callable(Then), ! }, 367 | eval_call(Then, [Bool], Result). 368 | 369 | foldfn(nil, _, EmptyVal, _, _, _, EmptyVal) --> []. 370 | foldfn([], _, EmptyVal, _, _, _, EmptyVal) --> []. 371 | foldfn([X|Xs], Fn, _, InitialVal, AccumulateGoal, Finalize, Result) --> 372 | foldfn_([X|Xs], Fn, InitialVal, AccumulateGoal, Finalize, Result). 373 | 374 | % variant of fold with witness (the value itself as FnResult-Value pairs) 375 | foldfnw(nil, _, EmptyVal, _, _, _, EmptyVal) --> []. 376 | foldfnw([], _, EmptyVal, _, _, _, EmptyVal) --> []. 377 | foldfnw([X|Xs], Fn, _, InitialVal, AccumulateGoal, Finalize, Result) --> 378 | foldfnw_([X|Xs], Fn, InitialVal, AccumulateGoal, Finalize, Result). 379 | 380 | 381 | foldfn_([], _, Cur, _, Finalize, Result) --> [], { call(Finalize, Cur, Result) }. 382 | foldfn_([X|Xs], Fn, Cur, Accumulate, Finalize, Result) --> 383 | eval_call(Fn, [X], Val), 384 | { call(Accumulate, Cur, Val, Cur1) }, 385 | foldfn_(Xs, Fn, Cur1, Accumulate, Finalize, Result). 386 | 387 | foldfnw_([], _, Cur, _, Finalize, Result) --> [], { call(Finalize, Cur, Result) }. 388 | foldfnw_([X|Xs], Fn, Cur, Accumulate, Finalize, Result) --> 389 | eval_call(Fn, [X], Val), 390 | { call(Accumulate, Cur, Val-X, Cur1) }, 391 | foldfnw_(Xs, Fn, Cur1, Accumulate, Finalize, Result). 392 | 393 | 394 | method(if, nil, [_], nil) --> []. 395 | method(if, false, [_], nil) --> []. 396 | method(if, Bool, [Then], Result) --> 397 | { \+ falsy(Bool) }, 398 | eval_if(Bool, Then, Result). 399 | method(if, nil, [_, Else], Result) --> eval_if(nil, Else, Result). 400 | method(if, false, [_, Else], Result) --> eval_if(false, Else, Result). 401 | method(if, Bool, [Then, _], Result) --> 402 | { \+ falsy(Bool) }, 403 | eval_if(Bool, Then, Result). 404 | 405 | method(while, Fn, [Then], Result) --> 406 | eval_call(Fn, [], Bool), 407 | method('%while', Bool-Fn, Then, Result). 408 | method('%while', Bool-_, _, nil) --> { falsy(Bool) }. 409 | method('%while', Bool-Fn, Then, nil) --> 410 | { \+ falsy(Bool) }, 411 | eval_call(Then, [Bool], _), 412 | method(while, Fn, [Then], nil). 413 | method(swap, ref(ID), [Fn|Args], New) --> 414 | { ref_get(ref(ID), Current), 415 | append([Current],Args, CallArgs) }, 416 | eval_call(Fn, CallArgs, New), 417 | { ref_set(ref(ID), New) }. 418 | 419 | % takes an array of truth values and array of actions 420 | % runs/returns value corresponding to first true value 421 | method(cond, [], [Else], Result) --> 422 | eval_if(true, Else, Result). 423 | method(cond, [], [], nil) --> []. 424 | method(cond, [B|Bools], [_|Actions], Result) --> 425 | { falsy(B) }, 426 | method(cond, Bools, Actions, Result). 427 | method(cond, [B|_], [A|_], Result) --> 428 | { \+ falsy(B) }, 429 | eval_if(B, A, Result). 430 | 431 | method(keep, Lst, [Fn], Result) --> 432 | foldfn(Lst, Fn, [], [], keep_, reverse, Result). 433 | method(map, Lst, [Fn], Result) --> 434 | foldfn(Lst, Fn, [], [], map_, reverse, Result). 435 | method(count, Lst, [Fn], Result) --> 436 | foldfn(Lst, Fn, 0, 0, count_, '=', Result). 437 | method(sort, Lst, [Fn], Result) --> 438 | foldfnw(Lst, Fn, [], [], map_, keysort, Result0), 439 | { pairs_values(Result0, Result) }. 440 | 441 | method(map, map(M0), [Fn], map(M2)) --> 442 | { map_new(map(M1)), 443 | map_pairs(map(M0), Pairs) }, 444 | method('%mapvals'(Fn), map(M1), Pairs, map(M2)). 445 | method('%mapvals'(_), M, [], M) --> []. 446 | method('%mapvals'(Fn), map(M0), [K-V|KVs], map(M2)) --> 447 | eval_call(Fn, [V], V1), 448 | { map_put(map(M0), K, V1, map(M1)) }, 449 | method('%mapvals'(Fn), map(M1), KVs, map(M2)). 450 | method(some, nil, _, nil) --> []. 451 | method(some, [], _, nil) --> []. 452 | method(some, [H|T], [Fn], Result) --> 453 | eval_call(Fn, [H], Res0), 454 | { falsy(Res0) }, 455 | method(some, T, [Fn], Result). 456 | method(some, [H|_], [Fn], Result) --> 457 | eval_call(Fn, [H], Result), 458 | { \+ falsy(Result), ! }. 459 | 460 | method('all?', [], [_], true) --> []. % vacuous truth 461 | method('all?', [X|_], [Fn], false) --> eval_call(Fn, [X], Res), { falsy(Res) }. 462 | method('all?', [X|Xs], [Fn], Result) --> 463 | eval_call(Fn, [X], Res), { \+ falsy(Res) }, 464 | method('all?', Xs, [Fn], Result). 465 | 466 | method(mapcat, nil, _, []) --> []. 467 | method(mapcat, [], _, []) --> []. 468 | method(mapcat, [H|T], [Fn], Result) --> 469 | eval_call(Fn, [H], Hv), 470 | method(map, T, [Fn], Tvs), 471 | { append([Hv|Tvs], Result), ! }. 472 | 473 | method(sum, Lst, [Fn], Result) --> foldfn(Lst, Fn, 0, 0, plus, '=', Result). 474 | 475 | method(min, Lst, [Fn], Result) --> foldfn(Lst, Fn, nil, nil, min_, '=', Result). 476 | method(max, Lst, [Fn], Result) --> foldfn(Lst, Fn, nil, nil, max_, '=', Result). 477 | method(minw, Lst, [Fn], Result) --> foldfnw(Lst, Fn, nil, nil, minw_, pair_list, Result). 478 | method(maxw, Lst, [Fn], Result) --> foldfnw(Lst, Fn, nil, nil, maxw_, pair_list, Result). 479 | 480 | method(group, nil, _, M) --> [], { map_new(M) }. 481 | method(group, [], _, M) --> [], { map_new(M) }. 482 | method(group, [X|Xs], [Fn], M1) --> 483 | { map_new(M0) }, 484 | method('%group'(Fn), M0, [X|Xs], M1). 485 | method('%group'(_), M, [], M) --> []. 486 | method('%group'(Fn), M0, [X|Xs], M2) --> 487 | eval_call(Fn, [X], Group), 488 | { map_get(M0, Group, Current), 489 | eval_op('++',Current,[X], New), 490 | map_put(M0, Group, New, M1) }, 491 | method('%group'(Fn), M1, Xs, M2). 492 | 493 | method('splitw', L, [Fn], [Taken,Dropped]) --> 494 | method('takew', L, [Fn], Taken), 495 | method('dropw', L, [Fn], Dropped). 496 | 497 | method('takew', [], [_], []) --> []. 498 | method('takew', [X|Xs], [Fn], Result) --> 499 | eval_call(Fn, [X], Bool), 500 | method('%takew', [X|Xs], [Fn,Bool], Result). 501 | method('%takew', _, [_, Falsy], []) --> 502 | [], 503 | { falsy(Falsy) }. 504 | method('%takew', [X|Xs], [Fn, Truthy], [X|Result]) --> 505 | { \+ falsy(Truthy) }, 506 | method('takew', Xs, [Fn], Result). 507 | method('dropw', [], [_], []) --> []. 508 | method('dropw', [X|Xs], [Fn], Result) --> 509 | eval_call(Fn, [X], Bool), 510 | method('%dropw', [X|Xs], [Fn,Bool], Result). 511 | method('%dropw', Lst, [_, Falsy], Lst) --> 512 | [], 513 | { falsy(Falsy) }. 514 | method('%dropw', [_|Xs], [Fn,Truthy], Result) --> 515 | { \+ falsy(Truthy) }, 516 | method('dropw', Xs, [Fn], Result). 517 | 518 | % Fold for nil or empty list is just the initial value (or nil if none specified) 519 | method(fold, [], [_, Init], Init) --> []. 520 | method(fold, nil, [_, Init], Init) --> []. 521 | method(fold, [], [_Fn], nil) --> []. 522 | method(fold, nil, [_Fn], nil) --> []. 523 | 524 | method(fold, Lst, Args, Result) --> 525 | { is_list(Lst), length(Lst, L), L > 0, 526 | (Args=[Fn,Init] -> true; Args=[Fn], Init=nil) }, 527 | method('%fold', Lst, [Fn, Init], Result). 528 | 529 | method('%fold', [], [_Fn, Acc], Acc) --> []. 530 | method('%fold', [V|Vs], [Fn, Acc], Result) --> 531 | eval_call(Fn, [Acc, V], Res0), 532 | method('%fold', Vs, [Fn, Res0], Result). 533 | 534 | method(do, Lst, [Fn], Result) --> 535 | { (is_list(Lst); Lst=nil), ! }, 536 | foldfn(Lst, Fn, nil, nil, do_, '=', Result). 537 | method(do, map(M), [Fn], Result) --> 538 | { map_pairs(map(M), KVs) }, 539 | method('%do', KVs, [Fn], Result). 540 | method('%do', [], _, nil) --> []. 541 | method('%do', [K-V|KVs], [Fn], nil) --> 542 | eval_call(Fn, [K,V], _Result), 543 | method('%do', KVs, [Fn], nil). 544 | 545 | 546 | 547 | method(filter, [], _, []) --> []. 548 | method(filter, [H|T], [Fn], Result) --> 549 | eval_call(Fn, [H], Include), 550 | method(filter, T, [Fn], Rest), 551 | { \+ falsy(Include) -> Result=[H|Rest]; Result=Rest }. 552 | 553 | method(call, Fn, Args, Result) --> 554 | eval_call(Fn, Args, Result). 555 | 556 | method(use, Lib, [], nil) --> 557 | { file_codes(Lib, Codes), 558 | once(phrase(statements(Stmts), Codes)) }, 559 | eval_stmts(Stmts, nil, _). 560 | 561 | method(eval, Code, [], Result) --> 562 | { once(phrase(statements(Stmts), Code)) 563 | -> true 564 | ; string_codes(Str, Code), 565 | err('Eval error, parsing failed: "~w"', [Str]) }, 566 | eval_stmts(Stmts, nil, Result). 567 | 568 | method(match, [], [[]], []) --> []. 569 | method(match, [Spec|Specs], [Input], Out) --> 570 | { is_callable(Spec) }, 571 | eval_call(Spec, [Input], MatchResult), 572 | method('%match', Specs, MatchResult, Out), !. 573 | 574 | % If previous match method call failed, catch it and return nil 575 | method(match, [Spec|_Specs], [_Input], nil) --> 576 | [], 577 | { is_callable(Spec) }. 578 | 579 | method(match, [Spec|Specs], [Input], Result) --> 580 | { is_list(Spec), append(Spec, Rest, Input) }, 581 | method(match, Specs, [Rest], Result). 582 | method(match, [Spec|_], [Input], nil) --> 583 | { is_list(Spec), \+ append(Spec, _, Input) }. 584 | 585 | % Don't continue if no match, result of whole match is nil 586 | method('%match', _, nil, nil) --> []. 587 | method('%match', Specs, [R|Rest], Out) --> 588 | method(match, Specs, Rest, Result), 589 | { Result = nil 590 | -> Out = nil 591 | ; (R = nil 592 | -> Out=Result 593 | ; append([R], Result, Out)) }. 594 | 595 | 596 | method(Name, My, Args, Result) --> 597 | { class(My, C), 598 | get_record_method(C, Name, Fun, Args, Args1) }, 599 | eval_call_my(Fun, My, Args1, Result). 600 | 601 | 602 | % Any pure Prolog method, that doesn't need DCG evaluation context 603 | method(Method, Me, Args, Result) --> 604 | [], 605 | { method(Method, Me, Args, Result) }. 606 | 607 | method(digit, N, [], nil) :- \+ between(48, 57, N), !. 608 | method(digit, N, [], D) :- between(48,57,N), !, D is N - 48. 609 | method(print, Me, _, Me) :- outputln(Me), !. 610 | method(sum, Lst, [], Result) :- sum_list(Lst, Result), !. 611 | method(first, [H|_], [], H) :- !. 612 | method(first, [], [], nil) :- !. 613 | method(last, Lst, [], Last) :- last(Lst, Last), !. 614 | method(nth, Lst, [N], Nth) :- nth0(N, Lst, Nth), !. 615 | method(lines, File, [], Lines) :- file_lines(File, Lines). 616 | method(contents, File, [], Contents) :- file_codes(File, Contents). 617 | method(heads, [], [], []) :- !. 618 | method(heads, [H|T], [], [[H|T]|Heads]) :- method(heads, T, [], Heads), !. 619 | method(reverse, Lst, [], Rev) :- reverse(Lst,Rev), !. 620 | method(to, From, [To], Lst) :- findall(N, between(From,To,N), Lst), !. 621 | method(to, From, [To, _], []) :- From > To, !. 622 | method(to, From, [To, Inc], [From|Rest]) :- From1 is From + Inc, method(to, From1, [To, Inc], Rest), !. 623 | method(fmt, PatternCs, Args, Out) :- fmt(PatternCs, Args, Out), !. 624 | method(join, Lst, [Sep], Out) :- 625 | foldl({Sep}/[Item,Acc,Out]>>append([Acc, Sep, Item], Out), 626 | Lst, [], Intermediate), 627 | % Remove the separator before first element 628 | append(Sep, Out, Intermediate), !. 629 | method(split, Lst, [Sep], Result) :- 630 | (once(append([Start, Sep, Rest], Lst)) 631 | -> (method(split, Rest, [Sep], RestSplit), 632 | Result=[Start|RestSplit]) 633 | ; Result=[Lst]), !. 634 | method(len, nil, [], 0) :- !. 635 | method(len, [], [], 0) :- !. 636 | method(len, [L|Lst], [], Result) :- length([L|Lst], Result), !. 637 | method(len, map(M), [], Result) :- map_size(map(M), Result), !. 638 | method(at, map(M), [Key], Result) :- map_get(map(M), Key, Result), !. 639 | method(at, map(M), [Key, Default], Result) :- 640 | map_get(map(M), Key, Val), 641 | (Val = nil 642 | -> Result = Default 643 | ; Result = Val). 644 | method(put, map(M), [Key, Val | KVs], R) :- 645 | map_put(map(M), Key, Val, map(M1)), 646 | method(put, map(M1), KVs, R), !. 647 | method(put, map(M), [], map(M)) :- !. 648 | method(del, map(M), Keys, map(M1)) :- 649 | map_del(map(M), Keys, map(M1)), !. 650 | method(keys, map(M), [], Ks) :- 651 | map_keys(map(M), Ks), !. 652 | 653 | % Record fields act as getter 654 | method(Field, rec(Record,ID), [], Val) :- record_get(rec(Record,ID), Field, Val), !. 655 | 656 | % Record fields with 1 parameter act as setter 657 | method(Field, rec(Record,R0), [Val], rec(Record,R1)) :- 658 | record_set(rec(Record,R0), Field, Val, rec(Record,R1)), !. 659 | 660 | method('number?', N, [], Result) :- (number(N) -> Result=true; Result=false), !. 661 | method('list?', L, [], Result) :- (is_list(L) -> Result=true; Result=false), !. 662 | method('map?', M, [], Result) :- (M=map(_) -> Result=true; Result=false), !. 663 | 664 | method(floor, N, [], Result) :- Result is floor(N). 665 | method(ceil, N, [], Result) :- Result is ceil(N). 666 | method(round, N, [], Result) :- Result is round(N). 667 | 668 | method(not, false, [], true) :- !. 669 | method(not, nil, [], true) :- !. 670 | method(not, X, [], false) :- \+ falsy(X), !. 671 | 672 | method('starts?', Lst, [Prefix], Result) :- 673 | (append(Prefix, _, Lst) -> Result=true; Result=false), !. 674 | method('ends?', Lst, [Suffix], Result) :- 675 | (append(_, Suffix, Lst) -> Result=true; Result=false), !. 676 | method(inc, N, [], Result) :- Result is N + 1. 677 | method(dec, N, [], Result) :- Result is N - 1. 678 | method(abs, N, [], Result) :- Result is abs(N). 679 | method(max, [N|Ns], [], Result) :- max_list([N|Ns], Result). 680 | method(min, [N|Ns], [], Result) :- min_list([N|Ns], Result). 681 | method(sort, [N|Ns], [], Result) :- msort([N|Ns], Result). 682 | method(sortu, [N|Ns], [], Result) :- sort([N|Ns], Result). 683 | method(take, Lst, [N], Result) :- take(Lst, N, Result). 684 | method(drop, Lst, [N], Result) :- drop(Lst, N, Result). 685 | method(debug, X, [], X) :- debug, !. 686 | method('_0', [N|_], [], N). 687 | method('_1', [_,N|_], [], N). 688 | method('_2', [_,_,N|_], [], N). 689 | method('_3', [_,_,_,N|_], [], N). 690 | method('nil?', V, _, B) :- (V=nil -> B=true; B=false). 691 | method(ref, V, [], Ref) :- ref_new(Ref, V). 692 | method(val, ref(ID), [], Val) :- ref_get(ref(ID), Val). 693 | method(val, ref(ID), [Val], Val) :- ref_set(ref(ID), Val). 694 | method('in?', Candidate, [Lst], Val) :- 695 | memberchk(Candidate, Lst) -> Val = true; Val = false. 696 | method('has?', Lst, [Candidate], Val) :- 697 | memberchk(Candidate, Lst) -> Val = true; Val = false. 698 | method(index, Lst, [Candidate], -1) :- 699 | \+ memberchk(Candidate, Lst), !. 700 | method(index, Lst, [Candidate], Idx) :- 701 | once(append([Before, [Candidate], _], Lst)), 702 | length(Before, Idx). 703 | method(read, Lst, [], [Val, Rest]) :- 704 | phrase(value(val(Val)), Lst, Rest). 705 | method(else, Me, [Or], Val) :- 706 | falsy(Me) 707 | -> Val=Or 708 | ; Val=Me. 709 | method(ws, Lst, [], [nil,Result]) :- 710 | phrase(blanks, Lst, Result). 711 | method(part, [], _, []) :- !. 712 | method(part, Lst, [Size], [Lst]) :- length(Lst, L), L < Size, !. 713 | method(part, Lst, [Size], [First|Lists]) :- 714 | length(First, Size), 715 | append(First, Rest, Lst), 716 | method(part, Rest, [Size], Lists). 717 | method(part, Lst, [Size,_Skip], []) :- length(Lst, L), L < Size, !. 718 | method(part, Lst, [Size, Skip], [First|Lists]) :- 719 | length(First, Size), 720 | append(First, _, Lst), 721 | length(Drop, Skip), 722 | append(Drop, Rest, Lst), 723 | method(part, Rest, [Size,Skip], Lists). 724 | method('empty?', [], [], true) :- !. 725 | method('empty?', nil, [], true) :- !. 726 | method('empty?', map(M), [], true) :- rb_new(M0), M0=M, !. 727 | method('empty?', _, [], false) :- !. % anything else is not 728 | method(pr, X, [], X) :- 729 | prettyln(X), !. 730 | method(str, N, [], Str) :- number(N), atom_codes(N, Str), !. 731 | method(str, [], [], `[]`) :- !. 732 | method(str, [X], [], Str) :- method(str, X, [], XStr), append([`[`, XStr, `]`], Str), !. 733 | method(str, Lst, [], Str) :- is_list(Lst), maplist([X,S]>>method(str, X, [], S), Lst, Strs), 734 | join(`, `, Strs, Vals), append([`[`, Vals, `]`], Str). 735 | method(allpairs, Lst, [], Pairs) :- 736 | findall([A,B], (select(A, Lst, Rest), member(B, Rest)), Pairs). 737 | method(sign, N, [], Sign) :- number(N), sign(N, Sign). 738 | method(numdigits, N, [], Digits) :- atom_length(N, Digits). 739 | method(pow, Num, [N], Res) :- Res is Num ^ N. 740 | method('is?', rec(T,_), [record_ref(T)], true) :- !. 741 | method('is?', _, _, false) :- !. 742 | method('as-list', map(M), [], Result) :- 743 | map_pairs(map(M), Pairs), 744 | maplist([K-V,[K,V]]>>true, Pairs, Result). 745 | method('as-map', Lst, [], map(M)) :- 746 | maplist([[K,V],K-V]>>true, Lst, Pairs), 747 | map_pairs(map(M), Pairs). 748 | % for putting a breakpoint 749 | debug. 750 | 751 | % add all methods here 752 | method(if/1, "if(Then)\nIf recipient is truthy, return Then. If Then is a function, it is called with the recipient as argument and its return value is returned."). 753 | method(if/2, "if(Then,Else)\nIf recipient is truthy, return Then otherwise return Else. Both can be values or functions."). 754 | method(cond/_, "cond(Action1,...,ActionN, Else)\nTakes the first truthy value in recipient and runs the corresponding action or Else if no value is truthy. If Else is not specified, returns nil when no value is truthy."). 755 | method(keep/1,"keep(Fn)\nCall Fn on all values in recipient, returns list of all results that are not nil."). 756 | method(map/1,"map(Fn)\nCall Fn on all values in recipient and returns values as a list."). 757 | method(mapcat/1,"mapcat(Fn)\nCall Fn on all values in recipient appending all resulting lists in one list. Fn must return a list or nil."). 758 | method(group/1,"group(Fn)\nGroup values in recipient by Fn. Calls Fn on each value in recipient and returns map with the result as key and list of values having that result as value."). 759 | method(print/0,"Print value to standard output."). 760 | method(digit/0,"Return ASCII digit code as number, or nil if code is not a digit."). 761 | method(do/1,"do(Fn)\nCall Fn on each value in recipient, presumably for side effects. For maps, Fn is called with key and value as arguments. Returns nil."). 762 | method(sum/0,"Returns sum of all values in recipient."). 763 | method(filter/1,"filter(Fn)\nCall Fn on each value in recipient, return list of values where the result is truthy."). 764 | method(call/_,"Call recipient function with given arguments."). 765 | method(first/0,"Return first value in recipient."). 766 | method(last/0,"Return last value in recipient."). 767 | method(nth/0,"nth(N)\nReturn the Nth value in recipient."). 768 | method(lines/0,"Return list of lines in file."). 769 | method(heads/0,"Return successive heads of recipient."). 770 | method(reverse/0,"Return recipient reversed."). 771 | method(to/1,"to(Upto)\nReturn list of successive numbers from recipient to Upto."). 772 | method(to/2,"to(Upto, Skip)\nReturn list of successive numbers from recipient to Upto, incrementing with Skip."). 773 | method(fmt/_,"fmt(Arg1,...,ArgN)\nFormat recipient string with given parameters, any use \"%s\" and \"%d\" to mark argument strings and numbers."). 774 | method(join/1,"join(Sep)\nJoin recipient list of lists into one list with separator."). 775 | method(split/1,"split(Sep)\nsplit recipient list into sublists at each occurence of separator Sep."). 776 | method(len/0,"Returns the length of a list."). 777 | method(at/1,"at(Key)\nReturn value at key or nil if not present."). 778 | method(put/_,"put(Key1,Val1,...,KeyN,ValN)\nPut values into map, returns new map with added mappings."). 779 | method('number?'/0,"True if recipient is a number, false otherwise."). 780 | method('list?'/0,"True if recipient is a list, false otherwise."). 781 | method('map?'/0,"True if recipient is a map, false otherwise."). 782 | method(floor/0,"Return largest integer that is less than or equal to recipient."). 783 | method(ceil/0,"Return smallest integer that is greater than or equal to recipient."). 784 | method(round/0,"Return the closest integer to recipient."). 785 | method(not/0,"Return negated boolean of recipient."). 786 | method('starts?'/1,"starts?(Prefix)\nReturns true if recipient starts with Prefix, false otherwise."). 787 | method('ends?'/1,"ends(Suffix)\nReturns true if recipient ends with Suffix, false otherwise."). 788 | method(some/1,"some(Pred)\nReturns the first value in recipient where the result of calling Pred on it returns truthy, or nil otherwise."). 789 | method(inc/0,"Return the recipient incremented by 1."). 790 | method(dec/0,"Return the recipient decremented by 1."). 791 | method(abs/0,"Return the absolute value of recipient."). 792 | method(min/0,"Return the minimum value of a list of numbers."). 793 | method(max/0,"Return the maximum value of a list of numbers."). 794 | method(min/1,"min(Fn)\nCall Fn on each value in recipient, return the minimum value."). 795 | method(max/1,"max(Fn)\nCall Fn on each value in recipient, return the maximum value."). 796 | method(sort/0,"Return recipient as sorted."). 797 | method(sortu/0,"Return recipient as sorted without duplicates."). 798 | method(drop/1,"drop(N)\nReturn recipient without the first N values."). 799 | method(take/1,"take(N)\nReturn the first N values of recipient."). 800 | method(debug/0,"Print internal interpreter state."). 801 | method('_0'/0,"Return 1st value of recipient."). % first 802 | method('_1'/0,"Return 2nd value of recipient."). % second 803 | method('_2'/0,"Return 3rd value of recipient."). % third 804 | method('_3'/0,"Return 3rd value of recipient."). % fourth 805 | method(sort/1,"sort(Fn)\nCall Fn on each value in recipient, return recipient sorted by the return values."). 806 | method(minw/1,"minw(Fn)\nCall Fn on each value in recipient, return list containing the smallest result and the value it corresponds with."). 807 | method(maxw/1,"maxw(Fn)\nCall Fn on each value in recipient, return list containing the largest result and the value it corresponds with."). 808 | method(use/1, "Load file denoted by recipient as elf code.\nAll record types, methods and names defined in the file are available after this call."). 809 | method(fold/2, "fold(Fn, Init)\nCall Fn with Init and first value of recipient, then with successive return values and values in recipient until all values are processed. If Init is omitted, nil is used."). 810 | method('nil?'/0, "True if recipient is nil, false otherwise."). 811 | method(eval/0, "Eval recipient as elf code, returns the last value."). 812 | method(ref/0, "Create new mutable reference from recipient."). 813 | method(val/_, "Get or set current value of mutable reference."). 814 | method(swap/_, "swap(Fn,...Args)\nUpdate value of ref by calling Fn on its current value (and optional Args). Returns new value."). 815 | method(while/1, "while(Then)\nCall recipient fn repeatedly while it returns a truthy value, call Then with that value."). 816 | method('in?'/1, "in?(List)\nTrue if recipient is a member of List, false otherwise."). 817 | method('has?'/1, "has?(Item)\nTrue if Item is member of recipient list, false otherwise."). 818 | method('takew'/1, "takew(Pred)\nReturn items of recipient list while calling Pred on item returns truthy."). 819 | method('dropw'/1, "dropw(Pred)\nReturn items of recipient after the first Pred call on item returns falsy."). 820 | method('splitw'/1, "splitw(Pred)\nCombines takew and dropw. Return list of [taken, dropped]."). 821 | method(read/0, "Read an Elf value (number, string, boolean or nil) from recipient string. Returns a list containing the read value and the rest of the string."). 822 | method(else/1, "else(V)\nReturn recipient value if it is truthy, otherwise return V."). 823 | method(ws/0, "Parsing method to skip 0 or more whitespace characters. Returns [nil, rest] where rest is the string after the whitespace."). 824 | method(match/1, "match(Input)\nParse input by matching it to specs in recipient list. Each spec can be a list expected at this position or a function that parses from the current position. Parsing function must return a parsed value and the rest of the list. Returns list of all non-nil values parsed by parsing functions."). 825 | method(part/_, "part(Size,[Skip])\nPartition list into sublists of Size. If skip is omitted it is the same as Size. If skip is omitted, the last list may be shorter than the others if the input is not evenly sized. 826 | 827 | Example: 828 | [1,2,3,4] part(2,1) 829 | => [[1,2],[2,3],[3,4]] 830 | "). 831 | method(count/_, "count(Fn)\nCount the number of values in recipient where Fn returns truthy."). 832 | method('empty?'/0, "True if recipient is nil, the empty list or an empty map."). 833 | method('contents'/0, "Return contents of recipient file as string."). 834 | method(index/1, "index(Elt)\nReturns the first index of Elt in recipient list or -1 if it does not appear in the list."). 835 | method(pr/0, "Pretty print recipient. Returns recipient."). 836 | method(str/0, "Return readable representation of recipient as string."). 837 | method(allpairs/0, "Return all possible [A,B] lists where A and B are elements of recipient list."). 838 | method(sign/0, "Return -1, 0 or 1 if recipient is negative, zero or positive respectively."). 839 | method(numdigits/0, "Return the number of digits in a number, eg. 123 => 3"). 840 | method(pow/1, "pow(N)\nRaise recipient to Nth power."). 841 | method('is?'/1, "is?(RecordType)\nTrue iff recipient is an instance of RecordType."). 842 | method('as-list'/0, "Return recipient map as list of [k,v] entries."). 843 | method('as-map'/1, "Return recipient list of [k,v] entries as a map."). 844 | 845 | falsy(nil). 846 | falsy(false). 847 | 848 | % List accumulator goals 849 | keep_(Lst, nil, Lst) :- !. 850 | keep_(Lst, false, Lst) :- !. 851 | keep_(Lst, X, [X|Lst]) :- !. 852 | map_(Lst, Item, [Item|Lst]) :- !. 853 | do_(_, _, nil) :- !. 854 | count_(Acc, nil, Acc) :- !. 855 | count_(Acc, false, Acc) :- !. 856 | count_(Acc, _, Acc1) :- succ(Acc,Acc1). 857 | min_(nil, X, X) :- !. 858 | min_(L, R, Out) :- (L < R -> Out=L;Out=R), !. 859 | max_(nil, X, X) :- !. 860 | max_(L, R, Out) :- (L > R -> Out=L;Out=R), !. 861 | 862 | % min and max with witness 863 | minw_(nil, X, X) :- !. 864 | minw_(R0-W0, R1-_, R0-W0) :- R0 < R1, !. 865 | minw_(R0-_, R1-W1, R1-W1) :- R0 >= R1, !. 866 | 867 | maxw_(nil, X, X) :- !. 868 | maxw_(R0-W0, R1-_, R0-W0) :- R0 > R1, !. 869 | maxw_(R0-_, R1-W1, R1-W1) :- R0 =< R1, !. 870 | 871 | pair_list(L-R, [L, R]) :- !. 872 | 873 | 874 | 875 | 876 | % take&drop utils 877 | take([], _, []) :- !. 878 | take(_, 0, []) :- !. 879 | take([X|Xs], N, [X|Result]) :- 880 | N > 0, succ(N1, N), 881 | take(Xs, N1, Result). 882 | 883 | drop([], _, []) :- !. 884 | drop(L, 0, L) :- !. 885 | drop([_|Xs], N, Drop) :- 886 | N > 0, succ(N1, N), 887 | drop(Xs, N1, Drop). 888 | 889 | 890 | %% Top level runner interface 891 | 892 | initial_ctx(ctx(env{},[],nil)). 893 | 894 | exec(Stmts, Out) :- 895 | record_cleanup, 896 | ref_cleanup, 897 | initial_ctx(Ctx), 898 | once(phrase(eval_stmts(Stmts,nil,Out), [Ctx], _)). 899 | 900 | exec(Stmts, Out, ctx(E,A,P), CtxOut) :- 901 | phrase(eval_stmts(Stmts, P, Out), [ctx(E,A,P)], [CtxOut]). 902 | 903 | run(File) :- 904 | once(phrase_from_file(statements(Stmts), File)), 905 | exec(Stmts, _Out). 906 | 907 | main :- 908 | current_prolog_flag(argv, []), 909 | repl. 910 | main :- 911 | current_prolog_flag(argv, [Script]), 912 | run(Script). 913 | 914 | run_codes(Input, Out) :- 915 | %call_with_time_limit(1, (phrase(statements(Stmts), Input))), 916 | once(phrase(statements(Stmts), Input)), 917 | once(exec(Stmts,Out1)), 918 | format_result(Out1, Out). 919 | 920 | format_result(rec(Rec,ID), Out) :- record_dict(rec(Rec,ID), Out), !. 921 | format_result(map(ID), Out) :- map_pairs(map(ID), Out), !. 922 | format_result(Out, Out). 923 | 924 | run_string(Input, Out) :- 925 | string_codes(Input, Codes), 926 | run_codes(Codes, Out). 927 | 928 | run_string_pretty(Input, Out) :- 929 | run_string(Input, Result), 930 | with_output_to(string(Out), 931 | prettyln(Result)). 932 | 933 | repl_input(Codes) :- 934 | read_line_to_codes(user_input,Codes), 935 | (Codes = end_of_file -> halt; true). 936 | 937 | repl :- 938 | record_cleanup, 939 | initial_ctx(Ctx0), 940 | State = state(Ctx0), 941 | version(V), 942 | format('Welcome to Elf REPL (v~w).\nExit with Ctrl-d.\nNo one is coming to help you... have fun!\n\n', [V]), 943 | repeat, 944 | format("elf> ",[]), 945 | repl_input(Codes), 946 | (phrase(elf:statements(Stmts), Codes) 947 | -> (arg(1, State, ctx(E,A,P)), 948 | ((catch(exec(Stmts, Result, ctx(E,A,P), ctx(E1,_,_)), 949 | _Err, 950 | fail)) 951 | -> (with_output_to(string(Out), once(prettyln(Result))), 952 | format('~w\n', [Out]), 953 | nb_setarg(1, State, ctx(E1,[],Result))) 954 | ; format('Execution failed ¯\\_(ツ)_/¯\n',[])), 955 | fail) 956 | ; (format('Parse error (╯°□°)╯︵ ┻━┻\n', []), fail)). 957 | 958 | 959 | :- begin_tests(elf). 960 | :- use_module(examples, [ex/3]). 961 | 962 | prg("foo: \"jas6sn0\", foo keep(&digit)", [6,0]). 963 | prg("[4, 2, 0, 6, 9] sum", 21). 964 | prg("[6, 2] sum * 4", 32). 965 | prg("\"README.md\" lines first", `# Elf programming language`). 966 | prg("[1, 2, 3, 4] map({$ * 2})", [2, 4, 6, 8]). 967 | prg("[1, 2, 3, 4] heads", [[1,2,3,4],[2,3,4],[3,4],[4]]). 968 | prg("\"elf\" reverse", `fle`). 969 | prg("\"some4digt02here\" keep(&digit), (_ first * 10) + _ last", 42). 970 | prg("1 to(5) reverse", [5,4,3,2,1]). 971 | prg("42 to(69, 7)", [42,49,56,63]). 972 | prg("\"%s day with %d%% chance of rain\" fmt(\"sunny\",7)", 973 | `sunny day with 7% chance of rain`). 974 | prg("[\"foo\", \"bar\" ,\"baz\"] join(\" and \")", 975 | `foo and bar and baz`). 976 | prg("\"foo, quux, !\" split(\", \")", 977 | [`foo`, `quux`, `!`]). 978 | prg("(\"foo\" ++ \"bar\") len", 6). 979 | prg("([\"foo\" len] first > 1) if(\"big\")", `big`). 980 | prg("Elf{age}, e: Elf{age:665}, e age(e age + 1)", 'Elf'{age:666}). 981 | prg("[\"foo\",\"bar\",\"other\"] group(&len) at(5)", [`other`]). 982 | prg("m: %{\"foo\": 40} put(\"bar\",2), m at(\"foo\") + m at(\"bar\")", 42). 983 | prg("{a,b| a + b} call(40,2)", 42). 984 | prg("-42 abs", 42). 985 | prg("69 inc", 70). 986 | prg("-69 dec", -70). 987 | 988 | prg("[100,200] nth(0)", 100). 989 | prg("[100,200] reverse", [200,100]). 990 | prg("[\"foo\",\"bar\"] join(\" and \")", `foo and bar`). 991 | prg("\"foo&bar\" split(\"&\")", [`foo`,`bar`]). 992 | prg("\"foobar\" starts?(\"foo\")", true). 993 | prg("\"foobar\" starts?(\"bra\")", false). 994 | prg("\"foobar\" ends?(\"bar\")", true). 995 | prg("\"foobar\" ends?(\"asd\")", false). 996 | prg("[9,1,420,-6] max", 420). 997 | prg("[9,1,420,-6] min", -6). 998 | prg("[5,6,7,1,2,5] sort", [1,2,5,5,6,7]). 999 | prg("[5,6,7,1,2,5] sortu", [1,2,5,6,7]). 1000 | prg("[false,false,true,true] cond(1,2,3,4,5)", 3). 1001 | prg("[false,false,false,false] cond(1,2,3,4,5)", 5). 1002 | prg("[\"im\",\"so\",\"meta\",\"even\",\"this\",\"acronym...\"] mapcat({$ take(1)})", 1003 | `ismeta`). 1004 | prg("\"foobar\" drop(3)", `bar`). 1005 | prg("[[11,22,33],[44,55,66]] min(&first)", 11). 1006 | prg("[[11,22,33],[44,55,66]] max(&first)", 44). 1007 | prg("n: 5 ref, n swap(&inc)", 6). 1008 | prg("n: 20 ref, n swap({a,b| a + b}, 22)", 42). 1009 | prg("l: [] ref, n: 5 ref, { n swap(&dec) > 0 } while({ l val(l val ++ [n val]) }), l val", 1010 | [4,3,2,1]). 1011 | prg("\"examples/elves.elf\" use, elves max(&age)", 317). 1012 | prg("\"examples/elves.elf\" use, elves sort(&age) _0 age", 75). 1013 | prg("\"examples/elves.elf\" use, elves minw(&age) _1 name", `Biscuit Peppermint`). 1014 | prg("\"examples/elves.elf\" use, elves maxw(&age) first", 317). 1015 | prg("\"examples/elves.elf\" use, elves minw(&age) _1 greet(\"world\")", 1016 | `Hi world! My name is Biscuit Peppermint and I'm 75 years old!`). 1017 | prg("%{\"foo\": 41, \"bar\": 665} map(&inc) at(\"foo\")", 42). 1018 | prg("[4,6,32] fold({$1 + $2},0)", 42). 1019 | prg("[\"a\",\"b\",\"c!\"] fold({a,b| a ++ b})", `abc!`). 1020 | prg("n: 5, \"x: 11, n * x\" eval", 55). 1021 | 1022 | prg("\"24dec\" splitw(&digit)", [`24`, `dec`]). 1023 | prg("\"24dec\" read", [24, `dec`]). 1024 | prg("[&read, \"-\", &read, \"-\", &read] match(\"2024-12-24\")", [2024,12,24]). 1025 | prg("[\"mul\", &read] match(\"mul420\")", [420]). 1026 | prg("[\"mul\", &read] match(\"mil420\")", nil). 1027 | prg("[\"mul\", &read] match(\"mulx20\")", nil). 1028 | prg("n: nil, n else(10)", 10). 1029 | prg("n: 42, n else(123)", 42). 1030 | prg("[1,2,3] all?({$ > 0})", true). 1031 | prg("[1,0,3] all?({$ > 0})", false). 1032 | prg("[] all?(&print)", true). % vacuous truth 1033 | prg("[[1,2],[5,1],[10,42]] map(&<)", [true,false,true]). 1034 | prg("\"hello\" part(2,1)", [`he`,`el`,`ll`,`lo`]). 1035 | prg("\"hello\" part(2)", [`he`,`ll`,`o`]). 1036 | prg("[] part(666)", []). 1037 | prg("\"4digit20here\" count(&digit)", 3). 1038 | prg("[] empty?", true). 1039 | prg("[1] empty?", false). 1040 | prg("%{} empty?", true). 1041 | prg("%{\"foo\": 42} empty?", false). 1042 | prg("42 empty?", false). 1043 | prg("[\"hat\", \"fat\", \"cat\"] keep(`(c|h)at`)", [[`hat`,`h`], [`cat`,`c`]]). 1044 | prg("\"hello\" index(@l)", 2). 1045 | prg("[42,100] index(666)", -1). 1046 | prg("start: \"start\", %{start: 666, \"end\": 1234}, _ at(\"start\")", 666). 1047 | prg("[42,100] str", `[42, 100]`). 1048 | prg("666 str", `666`). 1049 | prg("Foo{a,b}, Foo{a:2} is?(Foo)", true). 1050 | prg("Foo{a,b}, 42 is?(Foo)", false). 1051 | prg("%{\"foo\": 42} as-list", [[`foo`,42]]). 1052 | prg("[[\"foo\", 42], [\"bar\", 666]] as-map at(\"bar\")", 666). 1053 | prg("Number.sq: {my * my}, 42 sq", 1764). 1054 | prg("List.frob: {my drop(1) ++ [my _0]}, \"Hello\" frob frob", `lloHe`). 1055 | err("n: 5, \"x: 11, n* \" eval", err('Eval error, parsing failed: "~w"', _)). 1056 | err("[1,2,3] scum", err('Method call failed: ~w/0', [scum])). 1057 | err("[1,2,3] mab(&inc)", err('Method call failed: ~w/~w', [mab, 1])). 1058 | 1059 | test(programs, [forall(prg(Source,Expected))]) :- 1060 | once(run_string(Source,Actual)), 1061 | Expected = Actual. 1062 | 1063 | 1064 | test(examples, [forall(ex(_Name,Source,Expected))]) :- 1065 | once(run_string(Source,Actual)), 1066 | Expected = Actual. 1067 | 1068 | test(errors, [forall(err(Code, Error))]) :- 1069 | catch(once(run_string(Code,_)), 1070 | Err, 1071 | Error=Err). 1072 | 1073 | :- end_tests(elf). 1074 | --------------------------------------------------------------------------------