From e92337df5496c19cfab4896aca7f572c9adf7ad4 Mon Sep 17 00:00:00 2001 From: elucent <9532786+elucent@users.noreply.github.com> Date: Tue, 18 Aug 2020 03:31:11 -0400 Subject: [PATCH 01/17] Committing from remote because the repl.it integration seems flawed. Initial commit! Woo! --- LICENSE | 19 ++ defs.h | 73 +++++++ dev-notes/PROJECT | 43 ++++ dev-notes/TODO | 138 +++++++++++++ env.cpp | 71 +++++++ env.h | 47 +++++ errors.cpp | 62 ++++++ errors.h | 42 ++++ eval.cpp | 172 ++++++++++++++++ eval.h | 14 ++ hash.cpp | 45 +++++ hash.h | 398 +++++++++++++++++++++++++++++++++++++ io.cpp | 363 ++++++++++++++++++++++++++++++++++ io.h | 127 ++++++++++++ lex.cpp | 155 +++++++++++++++ lex.h | 48 +++++ main | Bin 0 -> 179600 bytes main.cpp | 97 +++++++++ parse.cpp | 204 +++++++++++++++++++ parse.h | 15 ++ rc.cpp | 14 ++ rc.h | 76 ++++++++ slice.h | 139 +++++++++++++ source.cpp | 126 ++++++++++++ source.h | 44 +++++ str.cpp | 268 +++++++++++++++++++++++++ str.h | 70 +++++++ tests/example.bl | 6 + type.cpp | 196 +++++++++++++++++++ type.h | 113 +++++++++++ values.cpp | 487 ++++++++++++++++++++++++++++++++++++++++++++++ values.h | 170 ++++++++++++++++ vec.h | 147 ++++++++++++++ 33 files changed, 3989 insertions(+) create mode 100644 LICENSE create mode 100644 defs.h create mode 100644 dev-notes/PROJECT create mode 100644 dev-notes/TODO create mode 100644 env.cpp create mode 100644 env.h create mode 100644 errors.cpp create mode 100644 errors.h create mode 100644 eval.cpp create mode 100644 eval.h create mode 100644 hash.cpp create mode 100644 hash.h create mode 100644 io.cpp create mode 100644 io.h create mode 100644 lex.cpp create mode 100644 lex.h create mode 100644 main create mode 100644 main.cpp create mode 100644 parse.cpp create mode 100644 parse.h create mode 100644 rc.cpp create mode 100644 rc.h create mode 100644 slice.h create mode 100644 source.cpp create mode 100644 source.h create mode 100644 str.cpp create mode 100644 str.h create mode 100644 tests/example.bl create mode 100644 type.cpp create mode 100644 type.h create mode 100644 values.cpp create mode 100644 values.h create mode 100644 vec.h diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6d8371f --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020 The Basil Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/defs.h b/defs.h new file mode 100644 index 0000000..a88badf --- /dev/null +++ b/defs.h @@ -0,0 +1,73 @@ +#ifndef BASIL_DEFS_H +#define BASIL_DEFS_H + +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; + +// hash.h + +template +class set; + +template +class map; + +// io.h + +class stream; +class file; +class buffer; + +// slice.h + +template +struct pair; + +template +class const_slice; + +template +class slice; + +// str.h + +class string; + +// utf8.h + +struct uchar; +class ustring; + +// vec.h + +template +class vector; + +namespace basil { + struct Definition; + class Env; + + class Value; + class ListValue; + class SumValue; + class ProductValue; + class FunctionValue; + + class Type; + class SingletonType; + class ListType; + class SumType; + class ProductType; + class FunctionType; +} + +#endif \ No newline at end of file diff --git a/dev-notes/PROJECT b/dev-notes/PROJECT new file mode 100644 index 0000000..9e6c954 --- /dev/null +++ b/dev-notes/PROJECT @@ -0,0 +1,43 @@ +Project Structure +----------------- + +[Included library code] + +All of the following was implemented before the jam. None of Basil's +functionality is contained within these files, they simply provide an +independent implementation of classes in the C++ standard library. + + - defs.h: a few shared typedefs and forward declarations. + + - hash.h/cpp: a standard polymorphic hash function, + hash set, and hash map based on robin hood probing. + + - io.h/cpp: a suite of variadic io functions and a stream abstraction, + which is implemented both by a file wrapper class and an in-memory buffer. + + - slice.h: a slice type, for representing subranges of strings and + containers. + + - str.h/cpp: a resizable byte string type. implements small string + optimization and only allocates for strings greater than 15 + characters. + + - vec.h: a resizable vector type. + +[Compiler/interpreter code] + +Code for implementing Basil's compiler phases. + + - source.h/cpp: an abstraction over a Basil source file. + + - lex.h/cpp: token type and lexer. + + - parse.h/cpp: parser implementation. + + - ast.h/cpp: nodes for Basil's syntax tree. + + - value.h/cpp: a type for representing any Basil value at compile time. + + - eval.h/cpp: evaluates a syntax tree to a Basil value. + + - main.cpp: driver function for the Basil compiler and utilities. \ No newline at end of file diff --git a/dev-notes/TODO b/dev-notes/TODO new file mode 100644 index 0000000..399b9ce --- /dev/null +++ b/dev-notes/TODO @@ -0,0 +1,138 @@ +Langjam To-do List +------------------ + +Day 1. Lexer and Sources + ✓ Comments: ignore characters up to and including newline. + ✓ Delimiter Tokens (parens, brackets, etc) + ✓ Whitespace: throw away whitespace chars other than newline, which + is a token. + ✓ Integers: any number of decimal digits, terminated by a non-digit + character. + ✓ Symbols: any number of symbols, letters, or numbers. + - Must start with a symbol or letter. + - Cannot start with an underscore. + ✓ Prefix operators: +, -. + - Must be preceded by a space. + - Must be succeeded by a term with no space. + ✓ Dot: . + - Multiple consecutive dots (with no space) form a symbol. + - Otherwise, lexed as a unique dot token. + ✓ Colon: : + - When used as a prefix operator (preceded by space, succeeded + by no space) produces a quote token. + - Otherwise, lexed as a symbol. A symbol of only one colon is + a unique colon token (used for either blocks or infix typing). + +Examples: + +# a comment +( ) { } [ ] ; | # delimiters +1 24 0591 # integers +abc Abc123 ++ =!= # symbols ++12 -(1 + 2) # prefix operators +a . b ... # dot (first is dot token, second is symbol) +:quoted :: n : int # various colon usage + +Day 2. Parser and Error Handling + ✓ Line numbers: extend tokens with line and column information. + ✓ Error reporting: report errors with varargs at line and column. + ✓ Terminals + - Symbols + - Integers + ✓ Simple, parentheses-enclosed lists. + ✓ Lines: lines of terms at the top level are lists. + - Semicolons: group terms within lists into sublists. + ✓ Indentation: continuations and blocks. + - Indenting after a newline: continuation of the previous list. + - Indenting after a colon and newline: treat each indented line + as a list, add them to previous list. + ✓ Unary ops: plus, minus, quote. + - +x should generate (+ 0 x) + - -x should generate (- 0 x) + - :x should generate (quote x) + ✓ Container expressions: lists and sets. + - [1 2 3] should generate (make-list 1 2 3) + - {1 2 3} should generate (make-set 1 2 3) + ✓ Infix ops: dot and colon. + - x . y should generate (x y) + - x : y should generate (set-type x y) + ✓ Splicing: pipe. + - |+ 1 2| should generate (splice (+ 1 2)) + +Day 3? Environments + ✓ Create environment type: must be able to store information on + definitions in a scope. + - Start just storing the name of each definition, and some basic + parameters such as kind (is it a variable? procedure? macro?) + and additional information (number of arguments?). + - We can expand this later with types, type variables, and values. + ✓ Implement definition lookup, lexically-scoped. + - Create a root environment: stores built-in functions and variables. + - Create a top-level environment: stores global definitions for + a file. + - Visit each macro and procedure body and create local environments. + - Recursively visit definitions within each local environment. + +Day 4? Types + ✓ Scalar Types: integer and symbol. + - Type type: a type for types. + ✓ Void type: singleton type representing the absence of a value. + - Any type: wildcard type that all types can convert to. + ✓ Symbol values: assign integer values to each symbol. + - This lets symbols be represented by simple words at runtime. + ✓ List Types: type for list parameterized by element. + ✓ Sum Types: type for a value of one of several member types. + - Implicitly convertible to from any of its member types. + - Product Types: type for a value with multiple members. + - Used for multiple arguments? + - Function Types: type for procedure. + - Argument type and return type. + ✓ Type kinds: overall categories of type. + - For example, how do we identify if a given type is a function + type? A product type? + ✓ Type deduplication: avoid instantiating types multiple times. + - Implement hashing and equality for types. + - Implement a way of generating types of different kinds. + +Day 5? Values + ✓ Value type: represents any Basil value within the compiler. + - Has a Basil type. + - Stores a value based on the kind of said type. + - Integers and symbols can be represented plainly within the union. + - All compound types are GC'd with reference counting. + - Arithmetic operations: common math ops with dynamic type checking. + - Add, subtract, etc. + - Comparison operations. + - Equality is defined for integers and symbols trivially. Lists + and functions are equal if their addresses are equal. Sums are + equal if their current types are equal and their values are + equal. Product types are equal if all members are equal. + - Relational operators are only defined for integers. + ✓ List operations: get head and tail, create cons cells. + - Function call: take function value and arguments, produce value. + ✓ Print to stream: format any value and print it. + +Day 6? Evaluation I - Special Forms. + - Quote: return data representation of quoted code. + - Splice: turn value into code, add it to enclosing list. + - Will we need some kind of parent pointer for this? + - Variable definitions: bind values to names in the local + environment. + - Procedure definitions: create and bind function values to names in + the local environment. + - def (args...) ... + - Will evaluate body terms from first to last when given + arguments. + - Macro definitions: same as ordinary variables and procedures, but + should be visited and handled first. + - Implement macro expansion. + +Day 7? Evaluation II - Builtins. + - Arithmetic ops. + - Comparison ops. + - List ops. + - Function calls. + - Anonymous functions (lambda). + - Display function: prints one or more values. + +By now, we might have a pretty workable dynamically-typed language. \ No newline at end of file diff --git a/env.cpp b/env.cpp new file mode 100644 index 0000000..5e41971 --- /dev/null +++ b/env.cpp @@ -0,0 +1,71 @@ +#include "env.h" + +namespace basil { + Def::Def(bool is_macro_in, bool is_infix_in, + u8 arity_in, u8 precedence_in): + Def(Value(), is_macro_in, is_infix_in, arity_in, precedence_in) {} + + Def::Def(Value value_in, bool is_macro_in, bool is_infix_in, + u8 arity_in, u8 precedence_in): + is_macro(is_macro_in), is_infix(is_infix_in), + arity(arity_in), precedence(precedence_in), value(value_in) {} + + bool Def::is_procedure() const { + return arity > 0 && !is_macro; + } + + bool Def::is_variable() const { + return arity == 0 && !is_macro; + } + + bool Def::is_macro_procedure() const { + return arity > 0 && is_macro; + } + + bool Def::is_macro_variable() const { + return arity == 0 && is_macro; + } + + Env::Env(const ref& parent): + _parent(parent) {} + + void Env::def(const string& name, u8 arity) { + _defs[name] = Def(false, false, arity); + } + + void Env::def_macro(const string& name, u8 arity) { + _defs[name] = Def(true, false, arity); + } + + void Env::infix(const string& name, u8 precedence, u8 arity) { + _defs[name] = Def(false, true, arity, precedence); + } + + void Env::infix_macro(const string& name, u8 precedence, u8 arity) { + _defs[name] = Def(true, true, arity, precedence); + } + + void Env::def(const string& name, const Value& value, u8 arity) { + _defs[name] = Def(value, false, false, arity); // todo: detect arity + } + + void Env::infix(const string& name, const Value& value, + u8 arity, u8 precedence) { + _defs[name] = Def(value, false, true, arity, precedence); + // todo: detect arity + } + + const Def* Env::find(const string& name) const { + auto it = _defs.find(name); + if (it == _defs.end()) + return _parent ? _parent->find(name) : nullptr; + else return &it->second; + } + + Def* Env::find(const string& name) { + auto it = _defs.find(name); + if (it == _defs.end()) + return _parent ? _parent->find(name) : nullptr; + else return &it->second; + } +} \ No newline at end of file diff --git a/env.h b/env.h new file mode 100644 index 0000000..c6fe5a3 --- /dev/null +++ b/env.h @@ -0,0 +1,47 @@ +#ifndef BASIL_ENV_H +#define BASIL_ENV_H + +#include "defs.h" +#include "hash.h" +#include "str.h" +#include "rc.h" +#include "values.h" + +namespace basil { + struct Def { + bool is_macro; // is the definition a macro alias or procedure? + bool is_infix; // is the definition for an infix procedure? + u8 arity; // 0 is a variable or alias, 1 or higher is procedure. + u8 precedence; // precedence of infix procedure + Value value; + + Def(bool is_macro_in = false, bool is_infix_in = false, + u8 arity_in = 0, u8 precedence_in = 0); + Def(Value value_in, bool is_macro_in = false, + bool is_infix_in = false, u8 arity_in = 0, u8 precedence_in = 0); + bool is_procedure() const; + bool is_variable() const; + bool is_macro_procedure() const; + bool is_macro_variable() const; + }; + + class Env { + map _defs; + ref _parent; + public: + Env(const ref& parent = ref::null()); + + void def(const string& name, u8 arity = 0); + void def_macro(const string& name, u8 arity = 0); + void infix(const string& name, u8 precedence, u8 arity = 2); + void infix_macro(const string& name, u8 precedence, u8 arity = 2); + void def(const string& name, const Value& value, u8 arity = 0); + void infix(const string& name, const Value& value, u8 arity = 2, + u8 precedence = 0); + bool lookup(const string& name) const; + const Def* find(const string& name) const; + Def* find(const string& name); + }; +} + +#endif \ No newline at end of file diff --git a/errors.cpp b/errors.cpp new file mode 100644 index 0000000..fe58033 --- /dev/null +++ b/errors.cpp @@ -0,0 +1,62 @@ +#include "errors.h" +#include "vec.h" +#include "lex.h" + +namespace basil { + SourceLocation::SourceLocation(): + line(0), column(0), length(0) {} + + SourceLocation::SourceLocation(u32 line_in, u16 column_in, u16 length_in): + line(line_in), column(column_in), length(length_in) {} + + SourceLocation::SourceLocation(const Token& token): + line(token.line), column(token.column), length(token.value.size()) {} + + const SourceLocation NO_LOCATION = { 0, 0, 0 }; + + struct Error { + SourceLocation loc; + string message; + }; + + static vector errors; + + void err(SourceLocation loc, const string& message) { + errors.push({ loc, message }); + } + + u32 error_count() { + return errors.size(); + } + + void clear_errors() { + errors.clear(); + } + + void print_errors(stream& io) { + for (const Error& e : errors) { + if (e.loc.length != 0) + write(io, "[", e.loc.line + 1, ":", e.loc.column + 1, "] "); + writeln(io, e.message); + } + } + + void print_errors(stream& io, const Source& src) { + for (const Error& e : errors) { + if (e.loc.length != 0) + write(io, "[", e.loc.line + 1, ":", e.loc.column + 1, "] "); + writeln(io, e.message); + + if (e.loc.length == 0) continue; // skip source printing if no location + const auto& line = src.line(e.loc.line); + if (line.back() == '\n') write(io, '\t', line); + else writeln(io, '\t', line); + u32 first = e.loc.column, last = e.loc.column + e.loc.length; + u32 i = 0; + write(io, '\t'); + for (; i < first; i ++) write(io, ' '); + for (; i < last; i ++) write(io, '^'); + writeln(io, ""); + } + } +} \ No newline at end of file diff --git a/errors.h b/errors.h new file mode 100644 index 0000000..68775c4 --- /dev/null +++ b/errors.h @@ -0,0 +1,42 @@ +#ifndef BASIL_ERRORS_H +#define BASIL_ERRORS_H + +#include "defs.h" +#include "str.h" +#include "io.h" +#include "slice.h" + +namespace basil { + class Token; + class Source; + + struct SourceLocation { + u32 line; + u16 column, length; + + SourceLocation(); + SourceLocation(u32 line_in, u16 column_in, u16 length_in = 1); + SourceLocation(const Token& token); + // expand later with other types of objects + }; + + // has length of zero, indicates position not in source + extern const SourceLocation NO_LOCATION; + + void err(SourceLocation loc, const string& message); + u32 error_count(); + void clear_errors(); + void print_errors(stream& io); + void print_errors(stream& io, const Source& src); + + template + void err(SourceLocation loc, Args... args) { + buffer b; + write(b, args...); + string message; + while (b.peek()) message += b.read(); + basil::err(loc, message); + } +} + +#endif \ No newline at end of file diff --git a/eval.cpp b/eval.cpp new file mode 100644 index 0000000..4240aad --- /dev/null +++ b/eval.cpp @@ -0,0 +1,172 @@ +#include "eval.h" + +namespace basil { + Value builtin_add(ref env, const Value& args) { + return add(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_sub(ref env, const Value& args) { + return sub(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_mul(ref env, const Value& args) { + return mul(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_div(ref env, const Value& args) { + return div(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_rem(ref env, const Value& args) { + return rem(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_head(ref env, const Value& args) { + return head(args.get_product()[0]); + } + + Value builtin_tail(ref env, const Value& args) { + return tail(args.get_product()[0]); + } + + Value builtin_cons(ref env, const Value& args) { + return cons(args.get_product()[0], args.get_product()[1]); + } + + ref create_root_env() { + ref root; + root->def("nil", Value(VOID)); + root->def("+", Value(new FunctionValue(root, builtin_add)), 2); + root->def("-", Value(new FunctionValue(root, builtin_sub)), 2); + root->def("*", Value(new FunctionValue(root, builtin_mul)), 2); + root->def("/", Value(new FunctionValue(root, builtin_div)), 2); + root->def("%", Value(new FunctionValue(root, builtin_rem)), 2); + root->def("head", Value(new FunctionValue(root, builtin_head)), 1); + root->def("tail", Value(new FunctionValue(root, builtin_tail)), 1); + root->def("::", Value(new FunctionValue(root, builtin_cons)), 2); + root->def("cons", Value(new FunctionValue(root, builtin_cons)), 2); + return root; + } + + // stubs + + Value eval_list(ref env, const Value& list); + Value eval(ref env, Value term); + + // utilities + + vector to_vector(const Value& list) { + vector values; + const Value* v = &list; + while (v->is_list()) { + values.push(v->get_list().head()); + v = &v->get_list().tail(); + } + return values; + } + + void traverse_list(ref env, const Value& list, + void(*fn)(ref, const Value&)) { + const Value* v = &list; + while (v->is_list()) { + fn(env, v->get_list().head()); + v = &v->get_list().tail(); + } + } + + void traverse_list(ref env, Value& list, + void(*fn)(ref, Value&)) { + Value* v = &list; + while (v->is_list()) { + fn(env, v->get_list().head()); + v = &v->get_list().tail(); + } + } + + void handle_splice(ref env, Value& item) { + if (!item.is_list()) return; + Value h = head(item); + if (h.is_symbol() && h.get_symbol() == symbol_value("splice")) { + Value t = tail(item); + if (t.is_void()) item = Value(VOID); + else item = eval(env, head(tail(item))); + } + } + + // definition stuff + + Value eval_list(ref env, const Value& term) { + Value h = head(term); + if (h.is_symbol()) { + const string& name = symbol_for(h.get_symbol()); + if (name == "quote") { + return head(tail(term)); + } + else if (name == "def") { + vector values = to_vector(term); + if (values.size() < 2 || !values[1].is_symbol()) { + err(term.loc(), "Expected name in definition."); + return error(); + } + if (values.size() < 3) { + err(term.loc(), "Expected value in definition."); + return error(); + } + if (values.size() == 3) { // variable + env->def(symbol_for(values[1].get_symbol()), + eval(env, values[2])); + return Value(VOID); + } + else { + if (!values[2].is_list()) { + err(values[2].loc(), "Expected argument list in function."); + return error(); + } + vector args = to_vector(values[2]); + vector argnames; + for (Value v : args) + if (v.is_symbol()) argnames.push(v.get_symbol()); + ref function_env(env); + Value func(new FunctionValue(function_env, + argnames, values[3])); + env->def(symbol_for(values[1].get_symbol()), + func, args.size()); + return Value(VOID); + } + } + else if (env->find(name) && env->find(name)->is_procedure()) { + vector args; + const Value* v = &term.get_list().tail(); + while (v->is_list()) { + args.push(eval(env, v->get_list().head())); + v = &v->get_list().tail(); + } + if (args.size() != env->find(name)->arity) { + err(term.loc(), "Procedure expects ", env->find(name)->arity, + " arguments, ", args.size(), " provided."); + return error(); + } + return call(eval(env, h), Value(new ProductValue(args))); + } + } + err(term.loc(), "Could not evaluate list."); + return error(); + } + + Value eval(ref env, Value term) { + if (term.is_list()) { + traverse_list(env, term, handle_splice); + return eval_list(env, term); + } + else if (term.is_int()) return term; + else if (term.is_symbol()) { + const string& name = symbol_for(term.get_symbol()); + const Def* def = env->find(name); + if (def) return def->value; + else { + err(term.loc(), "Undefined variable '", name, "'."); + return error(); + } + } + } +} \ No newline at end of file diff --git a/eval.h b/eval.h new file mode 100644 index 0000000..7cde8d1 --- /dev/null +++ b/eval.h @@ -0,0 +1,14 @@ +#ifndef BASIL_EVAL_H +#define BASIL_EVAL_H + +#include "defs.h" +#include "values.h" +#include "env.h" + +namespace basil { + ref create_root_env(); + + Value eval(ref env, Value term); +} + +#endif \ No newline at end of file diff --git a/hash.cpp b/hash.cpp new file mode 100644 index 0000000..c0ff392 --- /dev/null +++ b/hash.cpp @@ -0,0 +1,45 @@ +#include "hash.h" + +u64 rotl(u64 u, u64 n) { + return (u << n) | ((u >> (64 - n)) & ~(-1 << n)); +} + +u64 rotr(u64 u, u64 n) { + return (u >> n) | ((u << (64 - n)) & (-1 & ~(-1 << n))); +} + +u64 raw_hash(const void* t, uint64_t size) { + u64 h = 13830991727719723691ul; + + const u64* words = (const u64*)t; + u32 i = 0; + for (; i < size / 8; ++ i) { + uint64_t u = *words * 4695878395758459391ul; + h -= u; + h ^= (h >> 23); + ++ words; + } + + const u8* bytes = (const u8*)words; + i *= 8; + for (; i < size; ++ i) { + uint64_t u = *bytes * 4695878395758459391ul; + h -= u; + h ^= (h >> 23); + ++ bytes; + } + return h ^ (h << 37); +} + +template<> +u64 hash(const char* const& s) { + u32 size = 0; + const char* sptr = s; + while (*sptr) ++ sptr, ++ size; + return raw_hash((const u8*)s, size); +} + +template<> +u64 hash(const string& s) { + return raw_hash((const u8*)s.raw(), s.size()); +} \ No newline at end of file diff --git a/hash.h b/hash.h new file mode 100644 index 0000000..bfbf49d --- /dev/null +++ b/hash.h @@ -0,0 +1,398 @@ +#ifndef BASIL_HASH_H +#define BASIL_HASH_H + +#include "defs.h" +#include "str.h" +#include "slice.h" +#include + +template +bool equals(const T& a, const T& b) { + return a == b; +} + +template +bool key_equals(const T& a, const T& b) { + return a.first == b.first; +} + +u64 rotl(u64 u, u64 n); +u64 rotr(u64 u, u64 n); +u64 raw_hash(const void* t, uint64_t size); + +template +u64 hash(const T& t) { + return raw_hash((const u8*)&t, sizeof(T)); +} + +template<> +u64 hash(const char* const& s); + +template<> +u64 hash(const string& s); + +template +u64 key_hash(const T& a) { + return hash(a.first); +} + +template +class set { + enum bucket_status { + EMPTY, GHOST, FILLED + }; + + struct bucket { + u8 data[sizeof(T)]; + bucket_status status; + + bucket(): status(EMPTY) { + // + } + + ~bucket() { + if (status == FILLED) (*(T*)data).~T(); + } + + bucket(const bucket& other): status(other.status) { + if (status == FILLED) new(data) T(*(T*)other.data); + } + + bucket& operator=(const bucket& other) { + if (this != &other) { + evict(); + status = other.status; + if (status == FILLED) new(data) T(*(T*)other.data); + } + return *this; + } + + inline const T& value() const { + return *(const T*)data; + } + + inline T& value() { + return *(T*)data; + } + + inline void fill(const T& value) { + if (status != FILLED) { + status = FILLED, new(data) T(value); + } + else this->value() = value; + } + + inline void evict() { + if (status == FILLED) { + (*(T*)data).~T(); + status = GHOST; + } + } + + inline void clear() { + if (status == FILLED) (*(T*)data).~T(); + status = EMPTY; + } + }; + + bucket* data; + u32 _size, _capacity, _mask; + bool (*equals)(const T&, const T&); + u64 (*hash)(const T&); + + u32 log2(u32 capacity) { + u32 ans = 0; + while (capacity > 1) capacity = capacity >> 2, ++ ans; + return ans; + } + + void init(u32 size) { + _size = 0, _capacity = size; + data = new bucket[size]; + } + + void swap(T& a, T& b) { + T t = a; + a = b; + b = t; + } + + void free() { + delete[] data; + } + + void copy(const bucket* bs) { + for (u32 i = 0; i < _capacity; ++ i) { + data[i] = bs[i]; + } + } + + void grow() { + bucket* old = data; + u32 oldsize = _capacity; + init(_capacity * 2); + _mask = (_mask << 1) | 1; + for (u32 i = 0; i < oldsize; ++ i) { + if (old[i].status == FILLED) insert(old[i].value()); + } + delete[] old; + } + +public: + set(bool (*equals_in)(const T&, const T&) = ::equals, + u64 (*hash_in)(const T&) = ::hash): + equals(equals_in), hash(hash_in) { + init(8); + _mask = 7; + } + + ~set() { + free(); + } + + set(const set& other): set(other.equals, other.hash) { + init(other._capacity); + _size = other._size, _mask = other._mask; + copy(other.data); + } + + set& operator=(const set& other) { + if (this != &other) { + free(); + init(other._capacity); + hash = other.hash, equals = other.equals; + _size = other._size, _mask = other._mask; + copy(other.data); + } + return *this; + } + + class const_iterator { + const bucket *ptr, *end; + friend class set; + public: + const_iterator(const bucket* ptr_in, const bucket* end_in): + ptr(ptr_in), end(end_in) { + // + } + + const T& operator*() const { + return ptr->value(); + } + + const T* operator->() const { + return &(ptr->value()); + } + + const_iterator& operator++() { + if (ptr != end) ++ ptr; + while (ptr != end && ptr->status != FILLED) ++ ptr; + return *this; + } + + const_iterator operator++(int) { + iterator it = *this; + operator++(); + return it; + } + + bool operator==(const const_iterator& other) const { + return ptr == other.ptr; + } + + bool operator!=(const const_iterator& other) const { + return ptr != other.ptr; + } + }; + + class iterator { + bucket *ptr, *end; + friend class set; + public: + iterator(bucket* ptr_in, bucket* end_in): ptr(ptr_in), end(end_in) { + // + } + + T& operator*() { + return ptr->value(); + } + + T* operator->() { + return &(ptr->value()); + } + + iterator& operator++() { + if (ptr != end) ++ ptr; + while (ptr != end && ptr->status != FILLED) ++ ptr; + return *this; + } + + iterator operator++(int) { + iterator it = *this; + operator++(); + return it; + } + + bool operator==(const iterator& other) const { + return ptr == other.ptr; + } + + bool operator!=(const iterator& other) const { + return ptr != other.ptr; + } + + operator const_iterator() const { + return const_iterator(ptr, end); + } + }; + + iterator begin() { + bucket *start = data, *end = data + _capacity; + while (start != end && start->status != FILLED) ++ start; + return iterator(start, end); + } + + const_iterator begin() const { + const bucket *start = data, *end = data + _capacity; + while (start != end && start->status != FILLED) ++ start; + return const_iterator(start, end); + } + + iterator end() { + return iterator(data + _capacity, data + _capacity); + } + + const_iterator end() const { + return const_iterator(data + _capacity, data + _capacity); + } + + void insert(const T& t) { + if (double(_size + 1) / double(_capacity) > 0.625) grow(); + u64 h = hash(t); + u64 dist = 0; + u64 i = h & _mask; + T item = t; + while (true) { + if (data[i].status == EMPTY || data[i].status == GHOST) { + data[i].fill(item); + ++ _size; + return; + } + + if (data[i].status == FILLED && equals(data[i].value(), item)) { + return; + } + + u64 other_dist = (i - (hash(data[i].value()) & _mask)) & _mask; + if (other_dist < dist) { + if (data[i].status == GHOST) { + data[i].fill(item); + ++ _size; + return; + } + swap(item, data[i].value()); + dist = other_dist; + } + i = (i + 1) & _mask; + ++ dist; + } + } + + void erase(const T& t) { + u64 h = hash(t); + u64 i = h & _mask; + while (true) { + if (data[i].status == EMPTY) return; + if (data[i].status == FILLED && equals(data[i].value(), t)) { + data[i].evict(); + -- _size; + return; + } + i = (i + 1) & _mask; + } + } + + const_iterator find(const T& t) const { + u64 h = hash(t); + u64 i = h & _mask; + while (true) { + if (data[i].status == EMPTY) return end(); + u64 dist = (i - h) & _mask; + u64 oh = hash(data[i].value()); + u64 other_dist = (i - (oh & _mask)) & _mask; + if (other_dist < dist) return end(); + if (data[i].status == FILLED && equals(data[i].value(), t)) { + return const_iterator(data + i, data + _capacity); + } + i = (i + 1) & _mask; + } + } + + iterator find(const T& t) { + u64 h = hash(t); + u64 i = h & _mask; + while (true) { + if (data[i].status == EMPTY) return end(); + u64 dist = (i - (h & _mask)) & _mask; + u64 oh = hash(data[i].value()); + u64 other_dist = (i - (oh & _mask)) & _mask; + if (other_dist < dist) return end(); + if (data[i].status == FILLED && equals(data[i].value(), t)) { + return iterator(data + i, data + _capacity); + } + i = (i + 1) & _mask; + } + } + + u32 size() const { + return _size; + } + + u32 capacity() const { + return _capacity; + } +}; + +template +class map : public set> { +public: + map(): set>(::key_equals>, ::key_hash>) { + // + } + + void put(const K& key, const V& value) { + set>::insert({ key, value }); + } + + void erase(const K& key) { + set>::erase({ key, V() }); + } + + V& operator[](const K& key) { + pair dummy = { key, V() }; + auto it = set>::find(dummy); + if (it == set>::end()) { + set>::insert(dummy); + } + return set>::find(dummy)->second; + } + + const V& operator[](const K& key) const { + pair dummy = { key, V() }; + auto it = set>::find(dummy); + if (it == set>::end()) { + return *(const V*)nullptr; + } + return set>::find(dummy)->second; + } + + typename set>::const_iterator find(const K& key) const { + return set>::find({ key, V() }); + } + + typename set>::iterator find(const K& key) { + return set>::find({ key, V() }); + } +}; + +#endif diff --git a/io.cpp b/io.cpp new file mode 100644 index 0000000..71ff4b7 --- /dev/null +++ b/io.cpp @@ -0,0 +1,363 @@ +#include "io.h" +#include "str.h" +#include + +bool exists(const char* path) { + FILE* f = fopen(path, "r"); + if (!f) return false; + else return fclose(f), true; +} + +file::file(const char* fname, const char* flags): + file(fopen(fname, flags)) { + // +} + +file::file(FILE* f_in): f(f_in), done(false) { + // +} + +file::~file() { + fclose(f); +} + +void file::write(u8 c) { + fputc(c, f); +} + +u8 file::read() { + if (done) return '\0'; + int i = fgetc(f); + if (i == EOF) done = true; + return i; +} + +u8 file::peek() const { + if (done) return '\0'; + int i = fgetc(f); + ungetc(i, f); + if (i == EOF) return '\0'; + return i; +} + +void file::unget(u8 c) { + ungetc(c, f); +} + +file::operator bool() const { + int i = fgetc(f); + ungetc(i, f); + return i != EOF; +} + +void buffer::init(u32 size) { + _start = 0, _end = 0, _capacity = size; + if (_capacity <= 8) data = buf; + else data = new u8[_capacity]; +} + +void buffer::free() { + if (_capacity > 8) delete[] data; +} + +void buffer::copy(u8* other, u32 size, u32 start, u32 end) { + while (start != end) { + data[_end ++] = other[start]; + start = (start + 1) & (size - 1); + } +} + +void buffer::grow() { + u8* old = data; + u32 oldcap = _capacity; + u32 oldstart = _start, oldend = _end; + init(_capacity *= 2); + copy(old, oldcap, oldstart, oldend); + if (oldcap > 8) delete[] old; +} + +buffer::buffer() { + init(8); +} + +buffer::~buffer() { + free(); +} + +buffer::buffer(const buffer& other) { + init(other._capacity); + copy(other.data, other._capacity, other._start, other._end); +} + +buffer& buffer::operator=(const buffer& other) { + if (this != &other) { + free(); + init(other._capacity); + copy(other.data, other._capacity, other._start, other._end); + } + return *this; +} + +void buffer::write(u8 c) { + if (((_end + 1) & (_capacity - 1)) == _start) grow(); + data[_end] = c; + _end = (_end + 1) & (_capacity - 1); +} + +u8 buffer::read() { + if (_start == _end) return '\0'; + u8 c = data[_start]; + _start = (_start + 1) & (_capacity - 1); + return c; +} + +u8 buffer::peek() const { + if (_start == _end) return '\0'; + return data[_start]; +} + +void buffer::unget(u8 c) { + _start = (_start - 1) & (_capacity - 1); + while (_start == _end) grow(), _start = (_start - 1) & (_capacity - 1); + data[_start] = c; +} + +u32 buffer::size() const { + return (i64(_end) - i64(_start)) & (_capacity - 1); +} + +u32 buffer::capacity() const { + return _capacity; +} + +buffer::operator bool() const { + return _start != _end; +} + +u8* buffer::begin() { + return data + _start; +} + +const u8* buffer::begin() const { + return data + _start; +} + +u8* buffer::end() { + return data + _end; +} + +const u8* buffer::end() const { + return data + _end; +} + + +file _stdin_file(stdin), _stdout_file(stdout); + +stream &_stdin = _stdin_file, &_stdout = _stdout_file; + +static u32 precision = 5; + +void setprecision(u32 p) { + precision = p; +} + +static void print_unsigned(stream& io, u64 n) { + u64 m = n, p = 1; + while (m / 10) m /= 10, p *= 10; + while (p) io.write('0' + (n / p % 10)), p /= 10; +} + +static void print_signed(stream& io, i64 n) { + if (n < 0) io.write('-'), n = -n; + print_unsigned(io, n); +} + +static void print_rational(stream& io, double d) { + if (d < 0) io.write('-'), d = -d; + print_unsigned(io, u64(d)); + io.write('.'); + double r = d - u64(d); + u32 p = precision, zeroes = 0; + bool isZero = r == 0; + while (r && p) { + r *= 10; + if (u8(r)) { + isZero = false; + while (zeroes) io.write('0'), -- zeroes; + io.write('0' + u8(r)); + } + else ++ zeroes; + r -= u8(r); + -- p; + } + if (isZero) io.write('0'); +} + +void write(stream& io, u8 c) { + io.write(c); +} + +void write(stream& io, u16 n) { + print_unsigned(io, n); +} + +void write(stream& io, u32 n) { + print_unsigned(io, n); +} + +void write(stream& io, u64 n) { + print_unsigned(io, n); +} + +void write(stream& io, i8 c) { + io.write(c); +} + +void write(stream& io, i16 n) { + print_signed(io, n); +} + +void write(stream& io, i32 n) { + print_signed(io, n); +} + +void write(stream& io, i64 n) { + print_signed(io, n); +} + +void write(stream& io, float f) { + print_rational(io, f); +} + +void write(stream& io, double d) { + print_rational(io, d); +} + +void write(stream& io, char c) { + io.write(c); +} + +void write(stream& io, bool b) { + write(io, b ? "true" : "false"); +} + +void write(stream& io, const u8* s) { + while (*s) io.write(*(s ++)); +} + +void write(stream& io, const char* s) { + while (*s) io.write(*(s ++)); +} + +void write(stream& io, const buffer& b) { + for (u8 c : b) io.write(c); +} + +bool isspace(u8 c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; +} + +static u64 read_unsigned(stream& io) { + while (isspace(io.peek())) io.read(); // consume leading spaces + u64 result = 0; + while (isspace(io.peek())) io.read(); + while (io.peek()) { + u8 c = io.peek(); + if (isspace(c)) break; + else if (c < '0' || c > '9') { + while (io.peek() && !isspace(io.peek())) io.read(); + return 0; + } + result *= 10; + result += (c - '0'); + io.read(); + } + return result; +} + +static i64 read_signed(stream& io) { + while (isspace(io.peek())) io.read(); // consume leading spaces + if (io.peek() == '-') return io.read(), -read_unsigned(io); + else return read_unsigned(io); +} + +static double read_float(stream& io) { + while (isspace(io.peek())) io.read(); // consume leading spaces + buffer b; + while (io.peek() && !isspace(io.peek()) && io.peek() != '.') { + b.write(io.read()); + } + if (io.peek() != '.') return read_unsigned((stream&)b); + else io.read(); + u64 i; + read(b, i); + double pow = 0.1; + double result = i; + while (io.peek() && !isspace(io.peek())) { + result += (io.read() - '0') * pow; + pow *= 0.1; + } + return result; +} + +void read(stream& io, u8& c) { + c = io.read(); +} + +void read(stream& io, u16& n) { + n = read_unsigned(io); +} + +void read(stream& io, u32& n) { + n = read_unsigned(io); +} + +void read(stream& io, u64& n) { + n = read_unsigned(io); +} + +void read(stream& io, i8& c) { + c = io.read(); +} + +void read(stream& io, i16& n) { + n = read_signed(io); +} + +void read(stream& io, i32& n) { + n = read_signed(io); +} + +void read(stream& io, i64& n) { + n = read_signed(io); +} + +void read(stream& io, float& f) { + f = read_float(io); +} + +void read(stream& io, double& d) { + d = read_float(io); +} + +void read(stream& io, char& c) { + c = io.read(); +} + +void read(stream& io, bool& c) { + while (isspace(io.peek())) io.read(); // consume leading spaces + string s; + while (!isspace(io.peek())) s += io.read(); + if (s == "true") c = true; + else if (s == "false") c = false; +} + +void read(stream& io, u8* s) { + while (isspace(io.peek())) io.read(); // consume leading spaces + while (u8 c = io.read()) *(s ++) = c; +} + +void read(stream& io, char* s) { + while (isspace(io.peek())) io.read(); // consume leading spaces + while (u8 c = io.read()) *(s ++) = c; +} diff --git a/io.h b/io.h new file mode 100644 index 0000000..6495828 --- /dev/null +++ b/io.h @@ -0,0 +1,127 @@ +#ifndef BASIL_IO_H +#define BASIL_IO_H + +#include "defs.h" +#include "stdio.h" + +class stream { +public: + virtual void write(u8 c) = 0; + virtual u8 read() = 0; + virtual u8 peek() const = 0; + virtual void unget(u8 c) = 0; + virtual operator bool() const = 0; +}; + +bool exists(const char* path); + +class file : public stream { + FILE* f; + bool done; +public: + file(const char* fname, const char* flags); + file(FILE* f_in); + ~file(); + file(const file& other) = delete; + file& operator=(const file& other) = delete; + + void write(u8 c) override; + u8 read() override; + u8 peek() const override; + void unget(u8 c) override; + operator bool() const override; +}; + +class buffer : public stream { + u8* data; + u32 _start, _end, _capacity; + u8 buf[8]; + + void init(u32 size); + void free(); + void copy(u8* other, u32 size, u32 start, u32 end); + void grow(); +public: + buffer(); + ~buffer(); + buffer(const buffer& other); + buffer& operator=(const buffer& other); + + void write(u8 c) override; + u8 read() override; + u8 peek() const override; + void unget(u8 c) override; + u32 size() const; + u32 capacity() const; + operator bool() const override; + u8* begin(); + const u8* begin() const; + u8* end(); + const u8* end() const; +}; + +extern stream &_stdin, &_stdout; +void setprecision(u32 p); + +void write(stream& io, u8 c); +void write(stream& io, u16 n); +void write(stream& io, u32 n); +void write(stream& io, u64 n); +void write(stream& io, i8 c); +void write(stream& io, i16 n); +void write(stream& io, i32 n); +void write(stream& io, i64 n); +void write(stream& io, float f); +void write(stream& io, double d); +void write(stream& io, char c); +void write(stream& io, bool b); +void write(stream& io, const u8* s); +void write(stream& io, const char* s); +void write(stream& io, const buffer& b); + +template +void print(const Args&... args) { + write(_stdout, args...); +} + +template +void write(stream& s, const T& t, const Args&... args) { + write(s, t); + write(s, args...); +} + +template +void println(const Args&... args) { + writeln(_stdout, args...); +} + +template +void writeln(stream& s, const Args&... args) { + write(s, args...); + write(s, '\n'); +} + +bool isspace(u8 c); + +void read(stream& io, u8& c); +void read(stream& io, u16& n); +void read(stream& io, u32& n); +void read(stream& io, u64& n); +void read(stream& io, i8& c); +void read(stream& io, i16& n); +void read(stream& io, i32& n); +void read(stream& io, i64& n); +void read(stream& io, float& f); +void read(stream& io, double& d); +void read(stream& io, char& c); +void read(stream& io, bool& b); +void read(stream& io, u8* s); +void read(stream& io, char* s); + +template +void read(stream& io, T& t, Args&... args) { + read(io, t); + read(io, args...); +} + +#endif \ No newline at end of file diff --git a/lex.cpp b/lex.cpp new file mode 100644 index 0000000..b85aafa --- /dev/null +++ b/lex.cpp @@ -0,0 +1,155 @@ +#include "lex.h" +#include "io.h" +#include "errors.h" +#include +#include + +namespace basil { + Token::Token(TokenType type_in, const const_slice& value_in, u32 line_in, u32 column_in): + value(value_in), type(type_in), line(line_in), column(column_in) {} + + Token::operator bool() const { + return type != T_NONE; + } + + static const char* TOKEN_NAMES[NUM_TOKEN_TYPES] = { + "none", "int", "symbol", "coeff", "left paren", "right paren", + "left bracket", "right bracket", "left brace", "right brace", + "semicolon", "dot", "colon", "pipe", "plus", "minus", "quote", + "newline" + }; + + static Token NONE = Token(T_NONE, const_slice{0, nullptr}, 0, 0); + + static TokenType DELIMITERS[128] = { + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 00 + T_NONE, T_NONE, T_NEWLINE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 08 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 10 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 18 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 20 + T_LPAREN, T_RPAREN, T_NONE, T_NONE, T_NONE, T_NONE, T_DOT, T_NONE, // 28 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 30 + T_NONE, T_NONE, T_COLON, T_SEMI, T_NONE, T_NONE, T_NONE, T_NONE, // 38 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 40 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 48 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 50 + T_NONE, T_NONE, T_NONE, T_LBRACK, T_NONE, T_RBRACK, T_NONE, T_NONE, // 58 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 60 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 68 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 70 + T_NONE, T_NONE, T_NONE, T_LBRACE, T_PIPE, T_RBRACE, T_NONE, T_NONE // 78 + }; + + bool isdelimiter(u8 ch) { + return DELIMITERS[ch]; + } + + // is valid char in symbol + bool issymbol(u8 ch) { + return isprint(ch) && !isdelimiter(ch) && !isspace(ch); + } + + // is valid first char in symbol + bool issymbolstart(u8 ch) { + return issymbol(ch) && !isdigit(ch) && ch != '_'; + } + + // is symbolic char (e.g. $, +, @), not a letter or digit + bool issymbolic(u8 ch) { + return issymbolstart(ch) && !isalpha(ch); + } + + Token scan(Source::View& view) { + const u8* start = view.pos(); + u32 start_col = view.col(), line = view.line(); + u8 ch = view.peek(); + + if (!ch) { + return NONE; + } + else if (ch == '#') { + while (view.peek() && view.peek() != '\n') view.read(); + return scan(view); + } + else if (ch == '.') { + while (view.peek() == '.') view.read(); + u32 len = view.pos() - start; + return Token(len > 1 ? T_SYMBOL : T_DOT, { len, start }, line, start_col); + } + else if (ch == ':') { + while (view.peek() == ':') view.read(); + u32 len = view.pos() - start; + if (len > 1) return Token(T_SYMBOL, { len, start }, line, start_col); + if (isspace(view.peek())) return Token(T_COLON, { 1, start }, line, start_col); + else return Token(T_QUOTE, { 1, start }, line, start_col); + } + else if (isdelimiter(ch)) { + view.read(); + return Token(DELIMITERS[ch], { 1, start }, line, start_col); + } + else if (issymbolstart(ch)) { + view.read(); + if (ch == '+' && !isspace(view.peek()) + && !issymbolic(view.peek())) + return Token(T_PLUS, { 1, start }, line, start_col); + else if (ch == '-' && !isspace(view.peek()) + && !issymbolic(view.peek())) + return Token(T_MINUS, { 1, start }, line, start_col); + + while (issymbol(view.peek())) view.read(); + return Token(T_SYMBOL, { u32(view.pos() - start), start }, line, start_col); + } + else if (isdigit(ch)) { + while (isdigit(view.peek())) view.read(); + if (isalpha(view.peek())) + return Token(T_COEFF, { u32(view.pos() - start), start }, line, start_col); + else if (isdelimiter(view.peek()) || isspace(view.peek())) + return Token(T_INT, { u32(view.pos() - start), start }, line, start_col); + else + err({ line, u16(view.col()) }, "Unexpected character in integer '", view.peek(), "'."); + } + else if (isspace(ch)) { + view.read(); + return scan(view); + } + else err({ line, u16(view.col()) }, "Unexpected character in input '", view.peek(), "'."); + return NONE; + } + + TokenView::TokenView(vector& tokens, + Source& source, bool repl): + _tokens(&tokens), i(0), _source(&source), _repl(repl) {} + + const Token& TokenView::peek() { + return i < _tokens->size() ? (*_tokens)[i] : NONE; + } + + const Token& TokenView::read() { + const Token& t = peek(); + i ++; + return t; + } + + TokenView::operator bool() const { + return i < _tokens->size(); + } + + bool TokenView::repl() const { + return _repl; + } + + void TokenView::expand() { + print(". "); + Source::View view = _source->expand(_stdin); + + while (view.peek()) + _tokens->push(scan(view)); + if (error_count()) + print_errors(_stdout, *_source), clear_errors(); + } +} + +void write(stream& io, const basil::Token& t) { + string s = escape(t.value); + write(io, "[", basil::TOKEN_NAMES[t.type], ": '", s, "' : ", t.line, " ", t.column, "]"); +} diff --git a/lex.h b/lex.h new file mode 100644 index 0000000..2db9eab --- /dev/null +++ b/lex.h @@ -0,0 +1,48 @@ +#ifndef BASIL_LEX_H +#define BASIL_LEX_H + +#include "defs.h" +#include "source.h" + +namespace basil { + enum TokenType : u8 { + T_NONE, + T_INT, T_SYMBOL, T_COEFF, + T_LPAREN, T_RPAREN, T_LBRACK, T_RBRACK, T_LBRACE, T_RBRACE, + T_SEMI, T_DOT, T_COLON, T_PIPE, + T_PLUS, T_MINUS, T_QUOTE, + T_NEWLINE, + NUM_TOKEN_TYPES + }; + + struct Token { + const_slice value; + TokenType type; + u32 line, column; + + Token(TokenType type_in, const const_slice& value_in, u32 line_in, u32 column_in); + operator bool() const; + }; + + class TokenView { + vector* _tokens; + u32 i; + Source* _source; + bool _repl; + public: + TokenView(vector& tokens, + Source& source, bool repl = false); + + const Token& peek(); + const Token& read(); + operator bool() const; + bool repl() const; + void expand(); + }; + + Token scan(Source::View& view); +} + +void write(stream& io, const basil::Token& t); + +#endif diff --git a/main b/main new file mode 100644 index 0000000000000000000000000000000000000000..a06624decfa860096e49f77d291e111ada1ba5a6 GIT binary patch literal 179600 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̈́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
eL^#w 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%0DXz^m~= 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 literal 0 HcmV?d00001 diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..d11b90e --- /dev/null +++ b/main.cpp @@ -0,0 +1,97 @@ +#include "values.h" +#include "lex.h" +#include "vec.h" +#include "parse.h" +#include "eval.h" + +// ubuntu and mac color codes, from +// https://stackoverflow.com/questions/9158150/colored-output-in-c/ +#define RESET "\033[0m" +#define BLACK "\033[30m" /* Black */ +#define RED "\033[31m" /* Red */ +#define GREEN "\033[32m" /* Green */ +#define YELLOW "\033[33m" /* Yellow */ +#define BLUE "\033[34m" /* Blue */ +#define MAGENTA "\033[35m" /* Magenta */ +#define CYAN "\033[36m" /* Cyan */ +#define WHITE "\033[37m" /* White */ +#define BOLDBLACK "\033[1m\033[30m" /* Bold Black */ +#define BOLDRED "\033[1m\033[31m" /* Bold Red */ +#define BOLDGREEN "\033[1m\033[32m" /* Bold Green */ +#define BOLDYELLOW "\033[1m\033[33m" /* Bold Yellow */ +#define BOLDBLUE "\033[1m\033[34m" /* Bold Blue */ +#define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */ +#define BOLDCYAN "\033[1m\033[36m" /* Bold Cyan */ +#define BOLDWHITE "\033[1m\033[37m" /* Bold White */ + +using namespace basil; + +int main() { + Source repl; + + ref root = create_root_env(); + ref global(root); + + while (true) { + print("? "); + Source::View view = repl.expand(_stdin); + + vector tokens; + while (view.peek()) + tokens.push(scan(view)); + if (error_count()) { + print_errors(_stdout, repl), clear_errors(); + return 1; + } + + vector lines; + TokenView tview(tokens, repl, true); + while (tview.peek()) + lines.push(parse_line(tview, tview.peek().column)); + if (error_count()) { + print_errors(_stdout, repl), clear_errors(); + return 1; + } + + vector results; + for (Value v : lines) results.push(eval(global, v)); + + if (error_count()) { + print_errors(_stdout, repl), clear_errors(); + return 1; + } + else println(BOLDBLUE, "= ", results.back(), RESET, "\n"); + } +} + +// Value append(Value a, Value b) { +// if (a.is_void()) return b; +// return cons(head(a), append(tail(a), b)); +// } + +// Value reverse(Value a, Value acc) { +// if (a.is_void()) return acc; +// else return reverse(tail(a), cons(head(a), acc)); +// } + +// Value reverse(Value a) { +// return ::reverse(a, empty()); +// } + + // Source src("tests/example.bl"); + + // Source::View v = src.begin(); + // while (v.peek()) { + // Token t = scan(v); + // print(t, t.type == T_NEWLINE ? "\n" : " "); + // } + + // Source repl; + + // while (true) { + // Source::View view = repl.expand(_stdin); + // while (view.peek()) + // println(scan(view)); + // if (error_count()) + // print_errors(_stdout, repl), clear_errors(); + // } \ No newline at end of file diff --git a/parse.cpp b/parse.cpp new file mode 100644 index 0000000..2e7ccd7 --- /dev/null +++ b/parse.cpp @@ -0,0 +1,204 @@ +#include "parse.h" +#include "errors.h" +#include + +namespace basil { + i64 parse_int(const string& s) { + static buffer b; + write(b, s); + i64 i; + read(b, i); + return i; + } + + Value flatten(Value term) { + return term.is_list() && head(term).is_list() && &term.get_list().head() == &term.get_list().tail() ? head(term) : term; + } + + Value parse_primary(TokenView& view, u32 indent); + + bool out_of_input(TokenView& view) { // returns true if out of input + if (view.repl()) { + view.expand(); + return false; + } + else { + err(view.peek(), "Unexpected end of input."); + return true; + } + } + + void parse_enclosed(TokenView& view, vector& terms, + TokenType terminator, u32 indent) { + while (view.peek().type != terminator) { + while (view.peek().type == T_NEWLINE) view.read(); + if (!view.peek() && out_of_input(view)) return; + terms.push(parse(view, indent)); + } + view.read(); + } + + void parse_block(TokenView& view, vector& terms, + u32 prev_indent, u32 indent) { + while (view.peek().column > prev_indent) { + terms.push(parse_line(view, indent, false)); + if (view.peek().type == T_NEWLINE + && view.peek().column > prev_indent) view.read(); + if (!view.peek() && out_of_input(view)) return; + } + } + + void parse_continuation(TokenView& view, vector& terms, + u32 prev_indent, u32 indent) { + while (!view.peek() || view.peek().column > prev_indent) { + while (view.peek().type != T_NEWLINE) + terms.push(parse(view, indent)); + if (!view.peek() && out_of_input(view)) return; + } + } + + Value apply_op(TokenView& view, Value lhs, Value rhs, TokenType op) { + switch (op) { + case T_COLON: { + Value v = list_of(string("annotate"), lhs, rhs); + v.set_location(lhs.loc()); + return v; + } + case T_DOT: { + Value v = list_of(lhs, rhs); + v.set_location(lhs.loc()); + return v; + } + default: + err(view.peek(), "Unexpected binary operator '", + view.peek().value, "'."); + view.read(); + return Value(ERROR); + } + } + + Value parse_binary(TokenView& view, Value lhs, TokenType prev_op, + u32 indent) { + Value rhs = parse_primary(view, indent); + TokenType next_op = view.peek().type; + if (next_op == T_COLON || next_op == T_DOT) { + view.read(); + if (next_op == T_COLON && prev_op == T_DOT) // : has higher + // precedence + return apply_op(view, lhs, + parse_binary(view, rhs, next_op, indent), prev_op); + else + return parse_binary(view, + apply_op(view, lhs, rhs, prev_op), next_op, indent); + } + return apply_op(view, lhs, rhs, prev_op); + } + + Value parse_primary(TokenView& view, u32 indent) { + SourceLocation first = view.peek(); + switch (view.peek().type) { + case T_INT: { + Value v(parse_int(view.read().value)); + v.set_location(first); + return v; + } + case T_SYMBOL: { + Value v(string(view.read().value)); + v.set_location(first); + return v; + } + case T_PLUS: { + view.read(); + Value v = list_of(Value("+"), 0, parse_primary(view, indent)); + v.set_location(first); + return v; + } + case T_MINUS: { + view.read(); + Value v = list_of(Value("-"), 0, parse_primary(view, indent)); + v.set_location(first); + return v; + } + case T_QUOTE: { + view.read(); + Value v = list_of(Value("quote"), parse_primary(view, indent)); + v.set_location(first); + return v; + } + case T_LPAREN: { + view.read(); + vector terms; + parse_enclosed(view, terms, T_RPAREN, indent); + Value v = list_of(terms); + v.set_location(first); + return v; + } + case T_LBRACK: { + view.read(); + vector terms; + terms.push(Value("list-of")); + parse_enclosed(view, terms, T_RBRACK, indent); + Value v = list_of(terms); + v.set_location(first); + return v; + } + case T_LBRACE: { + view.read(); + vector terms; + terms.push(Value("set-of")); + parse_enclosed(view, terms, T_RBRACE, indent); + Value v = list_of(terms); + v.set_location(first); + return v; + } + case T_PIPE: { + view.read(); + vector terms; + parse_enclosed(view, terms, T_PIPE, indent); + Value v = terms.size() == 1 ? terms[0] : list_of(terms); + v.set_location(first); + return list_of(Value("splice"), v); + } + default: + err(view.peek(), "Unexpected token '", + escape(view.peek().value), "'."); + view.read(); + return error(); + } + } + + Value parse(TokenView& view, u32 indent) { + Value v = parse_primary(view, indent); + if (v.is_error()) return v; + if (view.peek().type == T_DOT) + view.read(), v = parse_binary(view, v, T_DOT, indent); + if (view.peek().type == T_COLON) { + view.read(); + if (view.peek().type == T_NEWLINE) { + view.read(); + vector terms; + terms.push(v); // label; + if (!view.peek() && out_of_input(view)) return error(); + if (view.peek().column > indent) + parse_block(view, terms, indent, view.peek().column); + return list_of(terms); + } + else v = parse_binary(view, v, T_COLON, indent); + } + return v; + } + + Value parse_line(TokenView& view, u32 indent, bool consume_line) { + SourceLocation first = view.peek(); + vector terms; + while (view.peek() && view.peek().type != T_NEWLINE) { + Value v = parse(view, indent); + if (v.is_error()) return error(); + else terms.push(v); + } + if (consume_line) view.read(); + Value v = terms.size() == 1 ? terms[0] : list_of(terms); + v.set_location(first); + return v; + } +} \ No newline at end of file diff --git a/parse.h b/parse.h new file mode 100644 index 0000000..0957cd0 --- /dev/null +++ b/parse.h @@ -0,0 +1,15 @@ +#ifndef BASIL_PARSE_H +#define BASIL_PARSE_H + +#include "defs.h" +#include "lex.h" +#include "vec.h" +#include "values.h" + +namespace basil { + Value parse(TokenView& view, u32 indent); + Value parse_line(TokenView& view, u32 indent, + bool consume_line = true); +} + +#endif \ No newline at end of file diff --git a/rc.cpp b/rc.cpp new file mode 100644 index 0000000..3c4e400 --- /dev/null +++ b/rc.cpp @@ -0,0 +1,14 @@ +#include "rc.h" + +RC::RC() : _count(1) {} + +RC::~RC() {} + +void RC::inc() { + _count ++; +} + +void RC::dec() { + _count --; + if (!_count) delete this; +} \ No newline at end of file diff --git a/rc.h b/rc.h new file mode 100644 index 0000000..a1bed81 --- /dev/null +++ b/rc.h @@ -0,0 +1,76 @@ +#ifndef BASIL_RC_H +#define BASIL_RC_H + +#include "defs.h" + +class RC { + u64 _count; +public: + RC(); + virtual ~RC(); + void inc(); + void dec(); +}; + +enum NullType { NULL_VALUE }; + +template +class ref { + struct BoxedValue { + u64 count; + T* data; + }* _value; + + ref(NullType): _value(nullptr) {} +public: + static constexpr ref null(); + template + ref(Args... args): _value(new BoxedValue{0, new T(args...)}) {} + + ~ref() { + if (_value && !--_value->count) { + delete _value->data; + delete _value; + } + } + + ref(const ref& other): _value(other._value) { + if (other._value) other._value->count ++; + } + + ref& operator=(const ref& other) { + if (other._value) other._value->count ++; + if (_value && !--_value->count) { + delete _value->data; + delete _value; + } + _value = other._value; + } + + const T& operator*() const { + return *_value->data; + } + + T& operator*() { + return *_value->data; + } + + const T* operator->() const { + return _value->data; + } + + T* operator->() { + return _value->data; + } + + operator bool() const { + return _value; + } +}; + +template +constexpr ref ref::null() { + return ref(NULL_VALUE); +} + +#endif \ No newline at end of file diff --git a/slice.h b/slice.h new file mode 100644 index 0000000..eb8943d --- /dev/null +++ b/slice.h @@ -0,0 +1,139 @@ +#ifndef BASIL_SLICE_H +#define BASIL_SLICE_H + +#include "defs.h" + +template +struct pair { + T first; + U second; + + pair(const T& _first, const U& _second): + first(_first), second(_second) {} + + bool operator==(const pair& other) const { + return first == other.first && second == other.second; + } +}; + +template +class const_slice { + u32 _size; + const T* _data; +public: + const_slice(u32 size, const T* data): + _size(size), _data(data) { + // + } + + const T& operator[](u32 i) const { + return _data[i]; + } + + const_slice operator[](pair range) const { + return { range.second - range.first, _data + range.first }; + } + + u32 size() const { + return _size; + } + + const T& front() const { + return _data[0]; + } + + const T& back() const { + return _data[_size - 1]; + } + + const T* begin() const { + return _data; + } + + const T* end() const { + return _data + _size; + } +}; + +template +class slice { + u32 _size; + T* _data; +public: + slice(u32 size, T* data): + _size(size), _data(data) { + // + } + + const T& operator[](u32 i) const { + return _data[i]; + } + + T& operator[](u32 i) { + return _data[i]; + } + + const_slice operator[](pair range) const { + return { range.second - range.first, _data + range.first }; + } + + slice operator[](pair range) { + return { range.second - range.first, _data + range.first }; + } + + u32 size() const { + return _size; + } + + const T& front() const { + return _data[0]; + } + + const T& back() const { + return _data[_size - 1]; + } + + T& front() { + return _data[0]; + } + + T& back() { + return _data[_size - 1]; + } + + const T* begin() const { + return _data; + } + + const T* end() const { + return _data + _size; + } + + T* begin() { + return _data; + } + + T* end() { + return _data + _size; + } + + operator const_slice() const { + return { _size, _data }; + } +}; + +template +const_slice span(const const_slice& a, const const_slice& b) { + const const_slice& from = a._data < b._data ? a : b; + const const_slice& to = a._data > b._data ? a : b; + return { to._size + to._data - from._data, from._data }; +} + +template +slice span(const slice& a, const slice& b) { + const slice& from = a._data < b._data ? a : b; + const slice& to = a._data > b._data ? a : b; + return { to._size + to._data - from._data, from._data }; +} + +#endif \ No newline at end of file diff --git a/source.cpp b/source.cpp new file mode 100644 index 0000000..31c9664 --- /dev/null +++ b/source.cpp @@ -0,0 +1,126 @@ +#include "source.h" +#include "io.h" + +namespace basil { + void Source::add_lines(string* s) { + const u8* p = s->raw(); + const u8* start = p; + u32 size = 0; + while (*p) { + size ++; // we're not at a null char, so this char should be included + if (*p == '\n') { + _lines.push({ size, start }); + start = p + 1; // next line starts after the \n + size = 0; + } + p ++; + } + if (size > 0) _lines.push({ size, start }); // in case file doesn't end with a newline + } + + void Source::calc_lines() { + _lines.clear(); + + for (string* s : _sections) { + add_lines(s); + } + } + + Source::Source() {} + + Source::Source(const char* filename) { + file f(filename, "r"); + if (!f) return; + + _sections.push(new string()); + while (f) { + if (f.peek() == '\t') *_sections.back() += " "; + else *_sections.back() += f.read(); + } + + calc_lines(); + } + + Source::~Source() { + for (string * s : _sections) delete s; + } + + Source::Source(const Source& other) { + for (string *s : _sections) delete s; + _sections.clear(); + for (string *s : other._sections) { + _sections.push(new string(*s)); + } + calc_lines(); + } + + Source& Source::operator=(const Source& other) { + if (this == &other) return *this; + for (string *s : _sections) delete s; + _sections.clear(); + for (string *s : other._sections) { + _sections.push(new string(*s)); + } + calc_lines(); + return *this; + } + + const_slice Source::line(u32 i) const { + return _lines[i]; + } + + const vector>& Source::lines() const { + return _lines; + } + + Source::View::View(const Source* const src, u32 line, u32 column): + _src(src), _line(line), _column(column) {} + + u8 Source::View::peek() const { + if (_line >= _src->lines().size()) return '\0'; + return _src->line(_line)[_column]; + } + + u8 Source::View::read() { + u8 ch = peek(); + if (!ch) return ch; // exit early on null char + + _column ++; + if (_column >= _src->line(_line).size()) + _column = 0, _line ++; + + return ch; + } + + const u8* Source::View::pos() const { + return &_src->line(_line)[_column]; + } + + u32 Source::View::col() const { + return _column; + } + + u32 Source::View::line() const { + return _line; + } + + Source::View Source::begin() const { + return Source::View(this, 0, 0); + } + + Source::View Source::expand(stream& io, u8 last) { + Source::View view = Source::View(this, _lines.size(), 0); + + string* line = new string(); + while (io.peek() != last) { + if (io.peek() == '\t') *line += " ", io.read(); + else *line += io.read(); + } + *line += io.read(); + + add_lines(line); + _sections.push(line); + + return view; + } +} \ No newline at end of file diff --git a/source.h b/source.h new file mode 100644 index 0000000..1b1cd9f --- /dev/null +++ b/source.h @@ -0,0 +1,44 @@ +#ifndef BASIL_SOURCE_H +#define BASIL_SOURCE_H + +#include "defs.h" +#include "vec.h" +#include "str.h" + +namespace basil { + class Source { + vector _sections; + vector> _lines; + + void add_lines(string* s); + void calc_lines(); + public: + Source(); + Source(const char* filename); + ~Source(); + Source(const Source& other); + Source& operator=(const Source& other); + + const_slice line(u32 i) const; + const vector>& lines() const; + + class View { + const Source* const _src; + u32 _line, _column; + + View(const Source* const src, u32 line, u32 column); + friend class Source; + public: + u8 peek() const; + u8 read(); + u32 line() const; + u32 col() const; + const u8* pos() const; + }; + + View begin() const; + View expand(stream& io, u8 last = '\n'); + }; +} + +#endif \ No newline at end of file diff --git a/str.cpp b/str.cpp new file mode 100644 index 0000000..eb33f2e --- /dev/null +++ b/str.cpp @@ -0,0 +1,268 @@ +#include "str.h" +#include "slice.h" +#include "io.h" +#include + +void string::free() { + if (_capacity > 16) delete[] data; +} + +void string::init(u32 size) { + if (size > 16) { + _size = 0, _capacity = size; + data = new u8[_capacity]; + } + else data = buf, _size = 0, _capacity = 16; + *data = '\0'; +} + +void string::copy(const u8* s) { + u8* dptr = data; + while (*s) *(dptr ++) = *(s ++), ++ _size; + *dptr = '\0'; +} + +void string::grow() { + u8* old = data; + init(_capacity * 2); + copy(old); + if (_capacity / 2 > 16) delete[] old; +} + +i32 string::cmp(const u8* s) const { + u8* dptr = data; + while (*s && *s == *dptr) ++ s, ++ dptr; + return i32(*dptr) - i32(*s); +} + +i32 string::cmp(const char* s) const { + u8* dptr = data; + while (*s && *s == *dptr) ++ s, ++ dptr; + return i32(*dptr) - i32(*s); +} + +string::string() { + init(16); +} + +string::~string() { + free(); +} + +string::string(const string& other) { + init(other._capacity); + copy(other.data); +} + +string::string(const char* s): string() { + operator+=(s); +} + +string::string(const const_slice& range): string() { + for (u8 c : range) operator+=(c); +} + +string& string::operator=(const string& other) { + if (this != &other) { + free(); + init(other._capacity); + copy(other.data); + } + return *this; +} + +string& string::operator+=(u8 c) { + if (_size + 1 >= _capacity) grow(); + data[_size ++] = c; + data[_size] = '\0'; + return *this; +} + +string& string::operator+=(char c) { + return operator+=(u8(c)); +} + +string& string::operator+=(const u8* s) { + u32 size = 0; + const u8* sptr = s; + while (*sptr) ++ size, ++ sptr; + while (_size + size + 1 >= _capacity) grow(); + u8* dptr = data + _size; + sptr = s; + while (*sptr) *(dptr ++) = *(sptr ++); + *dptr = '\0'; + _size += size; + return *this; +} + +string& string::operator+=(const char* s) { + return operator+=((const u8*)s); +} + +string& string::operator+=(const string& s) { + return operator+=(s.data); +} + +u32 string::size() const { + return _size; +} + +u32 string::capacity() const { + return _capacity; +} + +const u8& string::operator[](u32 i) const { + return data[i]; +} + +u8& string::operator[](u32 i) { + return data[i]; +} + +const_slice string::operator[](pair range) const { + return { range.second - range.first, data + range.first }; +} + +slice string::operator[](pair range) { + return { range.second - range.first, data + range.first }; +} + +const u8* string::raw() const { + return data; +} + +bool string::operator==(const u8* s) const { + return cmp(s) == 0; +} + +bool string::operator==(const char* s) const { + return cmp(s) == 0; +} + +bool string::operator==(const string& s) const { + return cmp(s.data) == 0; +} + +bool string::operator<(const u8* s) const { + return cmp(s) < 0; +} + +bool string::operator<(const char* s) const { + return cmp(s) < 0; +} + +bool string::operator<(const string& s) const { + return cmp(s.data) < 0; +} + +bool string::operator>(const u8* s) const { + return cmp(s) > 0; +} + +bool string::operator>(const char* s) const { + return cmp(s) > 0; +} + +bool string::operator>(const string& s) const { + return cmp(s.data) > 0; +} + +bool string::operator!=(const u8* s) const { + return cmp(s) != 0; +} + +bool string::operator!=(const char* s) const { + return cmp(s) != 0; +} + +bool string::operator!=(const string& s) const { + return cmp(s.data) != 0; +} + +bool string::operator<=(const u8* s) const { + return cmp(s) <= 0; +} + +bool string::operator<=(const char* s) const { + return cmp(s) <= 0; +} + +bool string::operator<=(const string& s) const { + return cmp(s.data) <= 0; +} + +bool string::operator>=(const u8* s) const { + return cmp(s) >= 0; +} + +bool string::operator>=(const char* s) const { + return cmp(s) >= 0; +} + +bool string::operator>=(const string& s) const { + return cmp(s.data) >= 0; +} + +string operator+(string s, char c) { + s += c; + return s; +} + +string operator+(string s, const char* cs) { + s += cs; + return s; +} + +string operator+(string s, const string& cs) { + s += cs; + return s; +} + +string escape(const string& s) { + string escaped = ""; + for (u32 i = 0; i < s.size(); i ++) { + switch (s[i]) { + case '\n': + escaped += "\\n"; + break; + case '\t': + escaped += "\\t"; + break; + case '\r': + escaped += "\\r"; + break; + case '\v': + escaped += "\\v"; + break; + case '\0': + escaped += "\\0"; + break; + case '\\': + escaped += "\\\\"; + break; + case '\"': + escaped += "\\\""; + break; + default: + escaped += s[i]; + } + } + return escaped; +} + +void write(stream& io, const string& s) { + write(io, s.raw()); +} + +void read(stream& io, string& s) { + while (isspace(io.peek())) io.read(); // consume leading spaces + while (io.peek() && !isspace(io.peek())) s += io.read(); +} + +void write(stream& io, const const_slice& str) { + for (u8 c : str) io.write(c); +} + +void write(stream& io, const slice& str) { + for (u8 c : str) io.write(c); +} diff --git a/str.h b/str.h new file mode 100644 index 0000000..5fdffdd --- /dev/null +++ b/str.h @@ -0,0 +1,70 @@ +#ifndef BASIL_STRING_H +#define BASIL_STRING_H + +#include "defs.h" +#include "stdio.h" + +class string { + u8* data; + u32 _size, _capacity; + u8 buf[16]; + + void free(); + void init(u32 size); + void copy(const u8* s); + void grow(); + i32 cmp(const u8* s) const; + i32 cmp(const char* s) const; + +public: + string(); + ~string(); + string(const string& other); + string(const char* s); + string(const const_slice& range); + string& operator=(const string& other); + + string& operator+=(u8 c); + string& operator+=(char c); + string& operator+=(const u8* s); + string& operator+=(const char* s); + string& operator+=(const string& s); + u32 size() const; + u32 capacity() const; + const u8& operator[](u32 i) const; + u8& operator[](u32 i); + const_slice operator[](pair range) const; + slice operator[](pair range); + const u8* raw() const; + bool operator==(const u8* s) const; + bool operator==(const char* s) const; + bool operator==(const string& s) const; + bool operator<(const u8* s) const; + bool operator<(const char* s) const; + bool operator<(const string& s) const; + bool operator>(const u8* s) const; + bool operator>(const char* s) const; + bool operator>(const string& s) const; + bool operator!=(const u8* s) const; + bool operator!=(const char* s) const; + bool operator!=(const string& s) const; + bool operator<=(const u8* s) const; + bool operator<=(const char* s) const; + bool operator<=(const string& s) const; + bool operator>=(const u8* s) const; + bool operator>=(const char* s) const; + bool operator>=(const string& s) const; +}; + +string operator+(string s, char c); +string operator+(string s, const char* cs); +string operator+(string s, const string& cs); +string escape(const string& s); + +void write(stream& io, const string& s); +void read(stream& io, string& s); + +void write(stream& io, const const_slice& str); +void write(stream& io, const slice& str); + +#endif \ No newline at end of file diff --git a/tests/example.bl b/tests/example.bl new file mode 100644 index 0000000..73c564d --- /dev/null +++ b/tests/example.bl @@ -0,0 +1,6 @@ +( ) { } [ ] ; | # delimiters +1 24 0591 # integers +abc Abc123 ++ =!= # symbols ++12 -(1 + 2) # prefix operators +a . b ... # dot (first is dot token, second is symbol) +:quoted :: n : int # various colon usage diff --git a/type.cpp b/type.cpp new file mode 100644 index 0000000..3f161af --- /dev/null +++ b/type.cpp @@ -0,0 +1,196 @@ +#include "type.h" + +namespace basil { + + Type::Type(u64 hash) : _hash(hash) {} + + u64 Type::hash() const { + return _hash; + } + + SingletonType::SingletonType(const string& repr) : Type(::hash(repr)), _repr(repr) {} + + TypeKind SingletonType::kind() const { + return KIND_SINGLETON; + } + + bool SingletonType::operator==(const Type& other) const { + return other.kind() == kind() && ((const SingletonType&) other)._repr == _repr; + } + + void SingletonType::format(stream& io) const { + write(io, _repr); + } + + ListType::ListType(const Type* element): + Type(element->hash() ^ 11340086872871314823ul), _element(element) {} + + const Type* ListType::element() const { + return _element; + } + + TypeKind ListType::kind() const { + return KIND_LIST; + } + + bool ListType::operator==(const Type& other) const { + return other.kind() == kind() && + ((const ListType&) other).element() == element(); + } + + void ListType::format(stream& io) const { + write(io, "[", _element, "]"); + } + + u64 set_hash(const set& members) { + u64 h = 6530804687830202173ul; + for (const Type* t : members) h ^= t->hash(); + return h; + } + + SumType::SumType(const set& members): + Type(set_hash(members)), _members(members) {} + + bool SumType::has(const Type* member) const { + return _members.find(member) != _members.end(); + } + + TypeKind SumType::kind() const { + return KIND_SUM; + } + + bool SumType::operator==(const Type& other) const { + if (other.kind() != kind()) return false; + for (const Type* t : _members) + if (!((const SumType&) other).has(t)) return false; + return true; + } + + void SumType::format(stream& io) const { + write(io, "("); + bool first = true; + for (const Type* t : _members) { + write(io, first ? "" : " | ", t); + first = false; + } + write(io, ")"); + } + + u64 vector_hash(const vector& members) { + u64 h = 10472618355682807153ul; + for (const Type* t : members) h ^= t->hash(); + return h; + } + + ProductType::ProductType(const vector& members): + Type(vector_hash(members)), _members(members) {} + + u32 ProductType::count() const { + return _members.size(); + } + + const Type* ProductType::member(u32 i) const { + return _members[i]; + } + + TypeKind ProductType::kind() const { + return KIND_PRODUCT; + } + + bool ProductType::operator==(const Type& other) const { + if (other.kind() != kind()) return false; + const ProductType& p = (const ProductType&)other; + if (p.count() != count()) return false; + for (u32 i = 0; i < count(); i ++) { + if (!(*p.member(i) == *member(i))) return false; + } + return true; + } + + void ProductType::format(stream& io) const { + write(io, "("); + bool first = true; + for (const Type* t : _members) { + write(io, first ? "" : " * ", t); + first = false; + } + write(io, ")"); + } + + FunctionType::FunctionType(const Type* arg, const Type* ret): + Type(arg->hash() ^ ret->hash() ^ 17623206604232272301ul), + _arg(arg), _ret(ret) {} + + const Type* FunctionType::arg() const { + return _arg; + } + + const Type* FunctionType::ret() const { + return _ret; + } + + u32 FunctionType::arity() const { + return _arg->kind() == KIND_PRODUCT ? + ((const ProductType*)_arg)->count() : 1; + } + + TypeKind FunctionType::kind() const { + return KIND_FUNCTION; + } + + bool FunctionType::operator==(const Type& other) const { + if (other.kind() != kind()) return false; + const FunctionType& f = (const FunctionType&)other; + return *f._arg == *_arg && *f._ret == *_ret; + } + + void FunctionType::format(stream& io) const { + write(io, "(", _arg, " -> ", _ret, ")"); + } + + struct TypeBox { + const Type* t; + + bool operator==(const TypeBox& other) const { + return *t == *other.t; + } + }; + + static map TYPEMAP; + + const Type* find_existing_type(const Type* t) { + auto it = TYPEMAP.find({ t }); + if (it == TYPEMAP.end()) return nullptr; + return it->second; + } + + const Type* create_type(const Type* t) { + TYPEMAP.put({ t }, t); + return t; + } + + const Type *INT = find("int"), + *SYMBOL = find("symbol"), + *VOID = find("void"), + *ERROR = find("error"), + *TYPE = find("type"); +} + +template<> +u64 hash(const basil::Type& t) { + return t.hash(); +} + +template<> +u64 hash(const basil::Type* const& t) { + return t->hash(); +} + +template<> +u64 hash(const basil::TypeBox& t) { + return t.t->hash(); +} + +void write(stream& io, const basil::Type* t) { + t->format(io); +} \ No newline at end of file diff --git a/type.h b/type.h new file mode 100644 index 0000000..c0ed17b --- /dev/null +++ b/type.h @@ -0,0 +1,113 @@ +#ifndef BASIL_TYPE_H +#define BASIL_TYPE_H + +#include "defs.h" +#include "str.h" +#include "hash.h" +#include "io.h" +#include "vec.h" + +namespace basil { + const u8 GC_KIND_FLAG = 128; + + enum TypeKind : u8 { + KIND_SINGLETON = 0, + KIND_LIST = GC_KIND_FLAG | 0, + KIND_SUM = GC_KIND_FLAG | 1, + KIND_PRODUCT = GC_KIND_FLAG | 2, + KIND_FUNCTION = GC_KIND_FLAG | 3 + }; + + class Type { + u64 _hash; + protected: + Type(u64 hash); + + public: + virtual TypeKind kind() const = 0; + u64 hash() const; + virtual bool operator==(const Type& other) const = 0; + virtual void format(stream& io) const = 0; + }; + + class SingletonType : public Type { + string _repr; + public: + SingletonType(const string& repr); + + TypeKind kind() const override; + bool operator==(const Type& other) const override; + void format(stream& io) const override; + }; + + class ListType : public Type { + const Type* _element; + public: + ListType(const Type* element); + + const Type* element() const; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + void format(stream& io) const override; + }; + + class SumType : public Type { + set _members; + public: + SumType(const set& members); + + bool has(const Type* member) const; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + void format(stream& io) const override; + }; + + class ProductType : public Type { + vector _members; + public: + ProductType(const vector& members); + + u32 count() const; + const Type* member(u32 i) const; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + void format(stream& io) const override; + }; + + class FunctionType : public Type { + const Type *_arg, *_ret; + public: + FunctionType(const Type* arg, const Type* ret); + + const Type* arg() const; + const Type* ret() const; + u32 arity() const; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + void format(stream& io) const override; + }; + + const Type* find_existing_type(const Type* t); + const Type* create_type(const Type* t); + + template + const Type* find(Args... args) { + T t(args...); + const Type* e = find_existing_type(&t); + + if (e) return e; + return create_type(new T(t)); + } + + extern const Type *INT, *SYMBOL, *VOID, *ERROR, *TYPE; +} + +template<> +u64 hash(const basil::Type& t); + +template<> +u64 hash(const basil::Type* const& t); + +void write(stream& io, const basil::Type* t); + +#endif \ No newline at end of file diff --git a/values.cpp b/values.cpp new file mode 100644 index 0000000..e1382fb --- /dev/null +++ b/values.cpp @@ -0,0 +1,487 @@ +#include "values.h" +#include "vec.h" +#include "env.h" +#include "eval.h" + +namespace basil { + static map symbol_table; + static vector symbol_array; + + u64 symbol_value(const string& symbol) { + static u64 count = 0; + auto it = symbol_table.find(symbol); + if (it == symbol_table.end()) { + symbol_table.put(symbol, count++); + symbol_array.push(symbol); + return count - 1; + } + else return it->second; + } + + const string& symbol_for(u64 value) { + return symbol_array[value]; + } + + Value::Value(): Value(VOID) {} + + Value::Value(const Type* type): + _type(type) {} + + Value::Value(i64 i, const Type* type): + _type(type) { + _data.i = i; + } + + Value::Value(const string& s, const Type* type): + _type(type) { + _data.u = symbol_value(s); + } + + Value::Value(const Type* type_value, const Type* type): + _type(type) { + _data.t = type_value; + } + + Value::Value(ListValue* l): + _type(find(l->head().type())) { + _data.rc = l; + } + + Value::Value(SumValue* s, const Type* type): + _type(type) { + _data.rc = s; + } + + Value::Value(ProductValue* p) { + vector ts; + for (const Value& v : *p) ts.push(v.type()); + _type = find(ts); + _data.rc = p; + } + + Value::Value(FunctionValue* f): + _type(find(INT, INT)) { + _data.rc = f; + } + + Value::~Value() { + if (_type->kind() & GC_KIND_FLAG) _data.rc->dec(); + } + + Value::Value(const Value& other): + _type(other._type), _loc(other._loc) { + _data.u = other._data.u; // copy over raw data + if (_type->kind() & GC_KIND_FLAG) _data.rc->inc(); + } + + Value& Value::operator=(const Value& other) { + if (other.type()->kind() & GC_KIND_FLAG) other._data.rc->inc(); + if (_type->kind() & GC_KIND_FLAG) _data.rc->dec(); + _type = other._type; + _loc = other._loc; + _data.u = other._data.u; // copy over raw data + return *this; + } + + bool Value::is_int() const { + return _type == INT; + } + + i64 Value::get_int() const { + return _data.i; + } + + i64& Value::get_int() { + return _data.i; + } + + bool Value::is_symbol() const { + return _type == SYMBOL; + } + + u64 Value::get_symbol() const { + return _data.u; + } + + u64& Value::get_symbol() { + return _data.u; + } + + bool Value::is_void() const { + return _type == VOID; + } + + bool Value::is_error() const { + return _type == ERROR; + } + + bool Value::is_type() const { + return _type == TYPE; + } + + const Type* Value::get_type() const { + return _data.t; + } + + const Type*& Value::get_type() { + return _data.t; + } + + bool Value::is_list() const { + return _type->kind() == KIND_LIST; + } + + const ListValue& Value::get_list() const { + return *(const ListValue*)_data.rc; + } + + ListValue& Value::get_list() { + return *(ListValue*)_data.rc; + } + + bool Value::is_sum() const { + return _type->kind() == KIND_SUM; + } + + const SumValue& Value::get_sum() const { + return *(const SumValue*)_data.rc; + } + + SumValue& Value::get_sum() { + return *(SumValue*)_data.rc; + } + + bool Value::is_product() const { + return _type->kind() == KIND_PRODUCT; + } + + const ProductValue& Value::get_product() const { + return *(const ProductValue*)_data.rc; + } + + ProductValue& Value::get_product() { + return *(ProductValue*)_data.rc; + } + + bool Value::is_function() const { + return _type->kind() == KIND_FUNCTION; + } + + const FunctionValue& Value::get_function() const { + return *(const FunctionValue*)_data.rc; + } + + FunctionValue& Value::get_function() { + return *(FunctionValue*)_data.rc; + } + + const Type* Value::type() const { + return _type; + } + + void Value::format(stream& io) const { + if (is_void()) write(io, "()"); + else if (is_error()) write(io, "error"); + else if (is_int()) write(io, get_int()); + else if (is_symbol()) write(io, symbol_for(get_symbol())); + else if (is_type()) write(io, get_type()); + else if (is_list()) { + bool first = true; + write(io, "("); + const Value* ptr = this; + while (ptr->is_list()) { + write(io, first ? "" : " ", ptr->get_list().head()); + ptr = &ptr->get_list().tail(); + first = false; + } + write(io, ")"); + } + else if (is_sum()) write(io, get_sum().value()); + else if (is_product()) { + bool first = true; + write(io, "("); + const Value* ptr = this; + while (ptr->is_list()) { + write(io, first ? "" : ", ", ptr->get_list().head()); + ptr = &ptr->get_list().tail(); + first = false; + } + write(io, ")"); + } + else if (is_function()) { + write(io, ""); + } + } + + u64 Value::hash() const { + if (is_void()) return 11103515024943898793ul; + else if (is_error()) return 14933118315469276343ul; + else if (is_int()) return ::hash(get_int()) ^ 6909969109598810741ul; + else if (is_symbol()) return ::hash(get_symbol()) ^ 1899430078708870091ul; + else if (is_type()) return get_type()->hash(); + else if (is_list()) { + u64 h = 9572917161082946201ul; + Value ptr = *this; + while (ptr.is_list()) { + h ^= ptr.get_list().head().hash(); + ptr = ptr.get_list().tail(); + } + return h; + } + return 0; + } + + bool Value::operator==(const Value& other) const { + if (type() != other.type()) return false; + else if (is_int()) return get_int() == other.get_int(); + else if (is_symbol()) return get_symbol() == other.get_symbol(); + else if (is_type()) return get_type() == other.get_type(); + else if (is_list()) { + const ListValue* l = &get_list(), *o = &other.get_list(); + do { + if (l->head() != o->head()) return false; + l = &l->tail().get_list(), o = &o->tail().get_list(); + } while (l->tail().is_list() && o->tail().is_list()); + return l->head() == o->head() && l->tail().is_void() && o->tail().is_void(); + } + return type() == other.type(); + } + + bool Value::operator!=(const Value& other) const { + return !(*this == other); + } + + void Value::set_location(SourceLocation loc) { + _loc = loc; + } + + SourceLocation Value::loc() const { + return _loc; + } + + ListValue::ListValue(const Value& head, const Value& tail) : + _head(head), _tail(tail) {} + + Value& ListValue::head() { + return _head; + } + + const Value& ListValue::head() const { + return _head; + } + + Value& ListValue::tail() { + return _tail; + } + + const Value& ListValue::tail() const { + return _tail; + } + + SumValue::SumValue(const Value& value): + _value(value) {} + + Value& SumValue::value() { + return _value; + } + + const Value& SumValue::value() const { + return _value; + } + + ProductValue::ProductValue(const vector& values): + _values(values) {} + + u32 ProductValue::size() const { + return _values.size(); + } + + Value& ProductValue::operator[](u32 i) { + return _values[i]; + } + + const Value& ProductValue::operator[](u32 i) const { + return _values[i]; + } + + const Value* ProductValue::begin() const { + return _values.begin(); + } + + const Value* ProductValue::end() const { + return _values.end(); + } + + Value* ProductValue::begin() { + return _values.begin(); + } + + Value* ProductValue::end() { + return _values.end(); + } + + FunctionValue::FunctionValue(ref env, const vector& args, + const Value& code): + _code(code), _builtin(nullptr), _env(env), _args(args) {} + + FunctionValue::FunctionValue(ref env, Builtin builtin): + _builtin(builtin), _env(env) {} + + const vector& FunctionValue::args() const { + return _args; + } + + bool FunctionValue::is_builtin() const { + return _builtin; + } + + Builtin FunctionValue::get_builtin() const { + return _builtin; + } + + ref FunctionValue::get_env() { + return _env; + } + + const ref FunctionValue::get_env() const { + return _env; + } + + const Value& FunctionValue::body() const { + return _code; + } + + Value binary_arithmetic(const Value& lhs, const Value& rhs, i64(*op)(i64, i64)) { + if (!lhs.is_int() && !lhs.is_error()) { + err(lhs.loc(), "Expected integer value in arithmetic expression, found '", + lhs.type(), "'."); + return error(); + } + if (!rhs.is_int() && !lhs.is_error()) { + err(rhs.loc(), "Expected integer value in arithmetic expression, found '", + rhs.type(), "'."); + return error(); + } + if (lhs.is_error() || rhs.is_error()) return error(); + + return Value(op(lhs.get_int(), rhs.get_int())); + } + + Value add(const Value& lhs, const Value& rhs) { + return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a + b; }); + } + + Value sub(const Value& lhs, const Value& rhs) { + return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a - b; }); + } + + Value mul(const Value& lhs, const Value& rhs) { + return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a * b; }); + } + + Value div(const Value& lhs, const Value& rhs) { + return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a / b; }); + } + + Value rem(const Value& lhs, const Value& rhs) { + return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a % b; }); + } + + Value head(const Value& v) { + if (!v.is_list() && !v.is_error()) { + err(v.loc(), "Can only get head of value of list type, given '", + v.type(), "'."); + return error(); + } + if (v.is_error()) return error(); + + return v.get_list().head(); + } + + Value tail(const Value& v) { + if (!v.is_list() && !v.is_error()) { + err(v.loc(), "Can only get tail of value of list type, given '", + v.type(), "'."); + return error(); + } + if (v.is_error()) return error(); + + return v.get_list().tail(); + } + + Value cons(const Value& head, const Value& tail) { + if (!tail.is_list() && !tail.is_void() && !tail.is_error()) { + err(tail.loc(), "Tail of cons cell must be a list or void, given '", + tail.type(), "'."); + return error(); + } + if (head.is_error() || tail.is_error()) return error(); + + return Value(new ListValue(head, tail)); + } + + Value empty() { + return Value(VOID); + } + + Value list_of(const Value& element) { + if (element.is_error()) return error(); + return cons(element, empty()); + } + + Value list_of(const vector& elements) { + Value l = empty(); + for (i64 i = elements.size() - 1; i >= 0; i --) { + l = cons(elements[i], l); + } + return l; + } + + Value error() { + return Value(ERROR); + } + + Value type_of(const Value& v) { + return Value(v.type(), TYPE); + } + + Value call(const Value& function, const Value& arg) { + if (!function.is_function() && !function.is_error()) { + err(function.loc(), "Called value is not a procedure."); + return error(); + } + if (!arg.is_product() && !arg.is_error()) { + err(arg.loc(), "Arguments not provided as a product."); + return error(); + } + if (function.is_error() || arg.is_error()) return error(); + + const FunctionValue& fn = function.get_function(); + if (fn.is_builtin()) { + return fn.get_builtin()(fn.get_env(), arg); + } + else { + ref env = fn.get_env(); + u32 argc = arg.get_product().size(), arity = fn.args().size(); + if (argc != arity) { + err(function.loc(), "Procedure requires ", arity, " arguments, ", + argc, " provided."); + return error(); + } + for (u32 i = 0; i < arity; i ++) { + const string& argname = symbol_for(fn.args()[i]); + env->find(argname)->value = arg.get_product()[i]; + } + return eval(env, fn.body()); + } + } +} + +template<> +u64 hash(const basil::Value& t) { + return t.hash(); +} + +void write(stream& io, const basil::Value& t) { + t.format(io); +} \ No newline at end of file diff --git a/values.h b/values.h new file mode 100644 index 0000000..36b0daf --- /dev/null +++ b/values.h @@ -0,0 +1,170 @@ +#ifndef BASIL_VALUES_H +#define BASIL_VALUES_H + +#include "defs.h" +#include "type.h" +#include "io.h" +#include "hash.h" +#include "errors.h" +#include "vec.h" +#include "rc.h" + +namespace basil { + u64 symbol_value(const string& symbol); + const string& symbol_for(u64 value); + + class ListValue; + class SumValue; + class ProductValue; + class FunctionValue; + + class Value { + const Type* _type; + union { + i64 i; + u64 u; + const Type* t; + RC* rc; + } _data; + SourceLocation _loc; + public: + Value(); + Value(const Type* type); + Value(i64 i, const Type* type = INT); + Value(const string& s, const Type* type = SYMBOL); + Value(const Type* type_value, const Type* type); + Value(ListValue* l); + Value(SumValue* s, const Type* type); + Value(ProductValue* p); + Value(FunctionValue* f); + ~Value(); + Value(const Value& other); + Value& operator=(const Value& other); + + bool is_int() const; + i64 get_int() const; + i64& get_int(); + + bool is_symbol() const; + u64 get_symbol() const; + u64& get_symbol(); + + bool is_void() const; + + bool is_error() const; + + bool is_type() const; + const Type* get_type() const; + const Type*& get_type(); + + bool is_list() const; + const ListValue& get_list() const; + ListValue& get_list(); + + bool is_sum() const; + const SumValue& get_sum() const; + SumValue& get_sum(); + + bool is_product() const; + const ProductValue& get_product() const; + ProductValue& get_product(); + + bool is_function() const; + const FunctionValue& get_function() const; + FunctionValue& get_function(); + + const Type* type() const; + void format(stream& io) const; + u64 hash() const; + bool operator==(const Value& other) const; + bool operator!=(const Value& other) const; + + void set_location(SourceLocation loc); + SourceLocation loc() const; + }; + + class ListValue : public RC { + Value _head, _tail; + public: + ListValue(const Value& head, const Value& tail); + + Value& head(); + const Value& head() const; + Value& tail(); + const Value& tail() const; + }; + + class SumValue : public RC { + Value _value; + public: + SumValue(const Value& value); + + Value& value(); + const Value& value() const; + }; + + class ProductValue : public RC { + vector _values; + public: + ProductValue(const vector& values); + + u32 size() const; + Value& operator[](u32 i); + const Value& operator[](u32 i) const; + const Value* begin() const; + const Value* end() const; + Value* begin(); + Value* end(); + }; + + using Builtin = Value (*)(ref, const Value& params); + + class FunctionValue : public RC { + Value _code; + Builtin _builtin; + ref _env; + vector _args; + public: + FunctionValue(ref env, const vector& args, + const Value& code); + FunctionValue(ref env, Builtin builtin); + + const vector& args() const; + const Value& body() const; + bool is_builtin() const; + Builtin get_builtin() const; + ref get_env(); + const ref get_env() const; + }; + + Value add(const Value& lhs, const Value& rhs); + Value sub(const Value& lhs, const Value& rhs); + Value mul(const Value& lhs, const Value& rhs); + Value div(const Value& lhs, const Value& rhs); + Value rem(const Value& lhs, const Value& rhs); + + Value head(const Value& v); + Value tail(const Value& v); + Value cons(const Value& head, const Value& tail); + Value empty(); + Value list_of(const Value& element); + + template + Value list_of(const Value& element, Args... args) { + return cons(element, list_of(args...)); + } + + Value list_of(const vector& elements); + + Value error(); + Value type_of(const Value& v); + + Value call(const Value& function, const Value& arg); +} + +template<> +u64 hash(const basil::Value& t); + +void write(stream& io, const basil::Value& t); + +#endif \ No newline at end of file diff --git a/vec.h b/vec.h new file mode 100644 index 0000000..e9a15d9 --- /dev/null +++ b/vec.h @@ -0,0 +1,147 @@ +#ifndef BASIL_VECTOR_H +#define BASIL_VECTOR_H + +#include "defs.h" +#include "slice.h" +#include + +template +class vector { + u8* data; + u32 _size, _capacity; + + void free(u8* array) { + T* tptr = (T*)array; + for (u32 i = 0; i < _size; i ++) tptr[i].~T(); + delete[] array; + } + + void init(u32 size) { + _size = 0, _capacity = size; + data = new u8[_capacity * sizeof(T)]; + } + + void copy(const T* ts, u32 n) { + _size = 0; + T* tptr = (T*)data; + for (u32 i = 0; i < n; i ++) { + new(tptr + i) T(ts[i]); + ++ _size; + } + } + + void destruct(u32 i) { + T* tptr = (T*)data; + tptr[i].~T(); + } + + void grow() { + u8* old = data; + u32 oldsize = _size; + init(_capacity * 2); + copy((const T*)old, oldsize); + free(old); + } + +public: + vector() { + init(16); + } + + vector(const const_slice& init): vector() { + for (const T& t : init) push(t); + } + + ~vector() { + free(data); + } + + vector(const vector& other) { + init(other._capacity); + copy((const T*)other.data, other._size); + } + + vector& operator=(const vector& other) { + if (this != &other) { + free(data); + init(other._capacity); + copy((const T*)other.data, other._size); + } + return *this; + } + + void push(const T& t) { + while (_size + 1 >= _capacity) grow(); + T* tptr = (T*)data; + new(tptr + _size) T(t); + ++ _size; + } + + void pop() { + -- _size; + destruct(_size); + } + + void clear() { + for (u32 i = 0; i < _size; ++ i) destruct(i); + _size = 0; + } + + const T& operator[](u32 i) const { + return ((T*)data)[i]; + } + + T& operator[](u32 i) { + return ((T*)data)[i]; + } + + const_slice operator[](pair range) const { + return { range.second - range.first, (T*)data + range.first }; + } + + slice operator[](pair range) { + return { range.second - range.first, (T*)data + range.first }; + } + + const T* begin() const { + return (const T*)data; + } + + T* begin() { + return (T*)data; + } + + const T* end() const { + return (const T*)data + _size; + } + + T* end() { + return (T*)data + _size; + } + + u32 size() const { + return _size; + } + + u32 capacity() const { + return _capacity; + } + + const T& front() const { + return *begin(); + } + + T& front() { + return *begin(); + } + + const T& back() const { + return *(end() - 1); + } + + T& back() { + return *(end() - 1); + } +}; + +#endif \ No newline at end of file From d931b86b80836c9ad0c2d4723fd28ad3c093da75 Mon Sep 17 00:00:00 2001 From: elucent <9532786+elucent@users.noreply.github.com> Date: Sat, 22 Aug 2020 04:54:43 -0400 Subject: [PATCH 02/17] Infix operators, macros are working! --- .gitignore | 2 + Makefile | 14 ++ defs.h | 20 ++ dev-notes/PROJECT | 43 ---- dev-notes/TODO | 138 ------------ env.cpp | 85 ++++++-- env.h | 34 +-- eval.cpp | 533 ++++++++++++++++++++++++++++++++++++++++------ lex.cpp | 11 +- lex.h | 1 + main | Bin 179600 -> 245824 bytes main.cpp | 83 +++----- parse.cpp | 31 ++- rc.h | 22 +- type.cpp | 39 +++- type.h | 28 ++- values.cpp | 300 ++++++++++++++++++++++++-- values.h | 71 +++++- 18 files changed, 1072 insertions(+), 383 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile delete mode 100644 dev-notes/PROJECT delete mode 100644 dev-notes/TODO diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c82b473 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +basil diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..630c8f3 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +SRCS := $(wildcard *.cpp) +OBJS := $(patsubst %.cpp,%.o,$(SRCS)) + +CXX := clang++ +CXXFLAGS := -std=c++11 -Os + +clean: + rm -f $(OBJS) basil + +basil: $(OBJS) + $(CXX) $(CXXFLAGS) $^ -o $@ + +%.o: %.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ diff --git a/defs.h b/defs.h index a88badf..b327d7a 100644 --- a/defs.h +++ b/defs.h @@ -13,6 +13,26 @@ typedef int16_t i16; typedef int32_t i32; typedef int64_t i64; +// ubuntu and mac color codes, from +// https://stackoverflow.com/questions/9158150/colored-output-in-c/ +#define RESET "\033[0m" +#define BLACK "\033[30m" /* Black */ +#define RED "\033[31m" /* Red */ +#define GREEN "\033[32m" /* Green */ +#define YELLOW "\033[33m" /* Yellow */ +#define BLUE "\033[34m" /* Blue */ +#define MAGENTA "\033[35m" /* Magenta */ +#define CYAN "\033[36m" /* Cyan */ +#define WHITE "\033[37m" /* White */ +#define BOLDBLACK "\033[1m\033[30m" /* Bold Black */ +#define BOLDRED "\033[1m\033[31m" /* Bold Red */ +#define BOLDGREEN "\033[1m\033[32m" /* Bold Green */ +#define BOLDYELLOW "\033[1m\033[33m" /* Bold Yellow */ +#define BOLDBLUE "\033[1m\033[34m" /* Bold Blue */ +#define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */ +#define BOLDCYAN "\033[1m\033[36m" /* Bold Cyan */ +#define BOLDWHITE "\033[1m\033[37m" /* Bold White */ + // hash.h template diff --git a/dev-notes/PROJECT b/dev-notes/PROJECT deleted file mode 100644 index 9e6c954..0000000 --- a/dev-notes/PROJECT +++ /dev/null @@ -1,43 +0,0 @@ -Project Structure ------------------ - -[Included library code] - -All of the following was implemented before the jam. None of Basil's -functionality is contained within these files, they simply provide an -independent implementation of classes in the C++ standard library. - - - defs.h: a few shared typedefs and forward declarations. - - - hash.h/cpp: a standard polymorphic hash function, - hash set, and hash map based on robin hood probing. - - - io.h/cpp: a suite of variadic io functions and a stream abstraction, - which is implemented both by a file wrapper class and an in-memory buffer. - - - slice.h: a slice type, for representing subranges of strings and - containers. - - - str.h/cpp: a resizable byte string type. implements small string - optimization and only allocates for strings greater than 15 - characters. - - - vec.h: a resizable vector type. - -[Compiler/interpreter code] - -Code for implementing Basil's compiler phases. - - - source.h/cpp: an abstraction over a Basil source file. - - - lex.h/cpp: token type and lexer. - - - parse.h/cpp: parser implementation. - - - ast.h/cpp: nodes for Basil's syntax tree. - - - value.h/cpp: a type for representing any Basil value at compile time. - - - eval.h/cpp: evaluates a syntax tree to a Basil value. - - - main.cpp: driver function for the Basil compiler and utilities. \ No newline at end of file diff --git a/dev-notes/TODO b/dev-notes/TODO deleted file mode 100644 index 399b9ce..0000000 --- a/dev-notes/TODO +++ /dev/null @@ -1,138 +0,0 @@ -Langjam To-do List ------------------- - -Day 1. Lexer and Sources - ✓ Comments: ignore characters up to and including newline. - ✓ Delimiter Tokens (parens, brackets, etc) - ✓ Whitespace: throw away whitespace chars other than newline, which - is a token. - ✓ Integers: any number of decimal digits, terminated by a non-digit - character. - ✓ Symbols: any number of symbols, letters, or numbers. - - Must start with a symbol or letter. - - Cannot start with an underscore. - ✓ Prefix operators: +, -. - - Must be preceded by a space. - - Must be succeeded by a term with no space. - ✓ Dot: . - - Multiple consecutive dots (with no space) form a symbol. - - Otherwise, lexed as a unique dot token. - ✓ Colon: : - - When used as a prefix operator (preceded by space, succeeded - by no space) produces a quote token. - - Otherwise, lexed as a symbol. A symbol of only one colon is - a unique colon token (used for either blocks or infix typing). - -Examples: - -# a comment -( ) { } [ ] ; | # delimiters -1 24 0591 # integers -abc Abc123 ++ =!= # symbols -+12 -(1 + 2) # prefix operators -a . b ... # dot (first is dot token, second is symbol) -:quoted :: n : int # various colon usage - -Day 2. Parser and Error Handling - ✓ Line numbers: extend tokens with line and column information. - ✓ Error reporting: report errors with varargs at line and column. - ✓ Terminals - - Symbols - - Integers - ✓ Simple, parentheses-enclosed lists. - ✓ Lines: lines of terms at the top level are lists. - - Semicolons: group terms within lists into sublists. - ✓ Indentation: continuations and blocks. - - Indenting after a newline: continuation of the previous list. - - Indenting after a colon and newline: treat each indented line - as a list, add them to previous list. - ✓ Unary ops: plus, minus, quote. - - +x should generate (+ 0 x) - - -x should generate (- 0 x) - - :x should generate (quote x) - ✓ Container expressions: lists and sets. - - [1 2 3] should generate (make-list 1 2 3) - - {1 2 3} should generate (make-set 1 2 3) - ✓ Infix ops: dot and colon. - - x . y should generate (x y) - - x : y should generate (set-type x y) - ✓ Splicing: pipe. - - |+ 1 2| should generate (splice (+ 1 2)) - -Day 3? Environments - ✓ Create environment type: must be able to store information on - definitions in a scope. - - Start just storing the name of each definition, and some basic - parameters such as kind (is it a variable? procedure? macro?) - and additional information (number of arguments?). - - We can expand this later with types, type variables, and values. - ✓ Implement definition lookup, lexically-scoped. - - Create a root environment: stores built-in functions and variables. - - Create a top-level environment: stores global definitions for - a file. - - Visit each macro and procedure body and create local environments. - - Recursively visit definitions within each local environment. - -Day 4? Types - ✓ Scalar Types: integer and symbol. - - Type type: a type for types. - ✓ Void type: singleton type representing the absence of a value. - - Any type: wildcard type that all types can convert to. - ✓ Symbol values: assign integer values to each symbol. - - This lets symbols be represented by simple words at runtime. - ✓ List Types: type for list parameterized by element. - ✓ Sum Types: type for a value of one of several member types. - - Implicitly convertible to from any of its member types. - - Product Types: type for a value with multiple members. - - Used for multiple arguments? - - Function Types: type for procedure. - - Argument type and return type. - ✓ Type kinds: overall categories of type. - - For example, how do we identify if a given type is a function - type? A product type? - ✓ Type deduplication: avoid instantiating types multiple times. - - Implement hashing and equality for types. - - Implement a way of generating types of different kinds. - -Day 5? Values - ✓ Value type: represents any Basil value within the compiler. - - Has a Basil type. - - Stores a value based on the kind of said type. - - Integers and symbols can be represented plainly within the union. - - All compound types are GC'd with reference counting. - - Arithmetic operations: common math ops with dynamic type checking. - - Add, subtract, etc. - - Comparison operations. - - Equality is defined for integers and symbols trivially. Lists - and functions are equal if their addresses are equal. Sums are - equal if their current types are equal and their values are - equal. Product types are equal if all members are equal. - - Relational operators are only defined for integers. - ✓ List operations: get head and tail, create cons cells. - - Function call: take function value and arguments, produce value. - ✓ Print to stream: format any value and print it. - -Day 6? Evaluation I - Special Forms. - - Quote: return data representation of quoted code. - - Splice: turn value into code, add it to enclosing list. - - Will we need some kind of parent pointer for this? - - Variable definitions: bind values to names in the local - environment. - - Procedure definitions: create and bind function values to names in - the local environment. - - def (args...) ... - - Will evaluate body terms from first to last when given - arguments. - - Macro definitions: same as ordinary variables and procedures, but - should be visited and handled first. - - Implement macro expansion. - -Day 7? Evaluation II - Builtins. - - Arithmetic ops. - - Comparison ops. - - List ops. - - Function calls. - - Anonymous functions (lambda). - - Display function: prints one or more values. - -By now, we might have a pretty workable dynamically-typed language. \ No newline at end of file diff --git a/env.cpp b/env.cpp index 5e41971..7deef36 100644 --- a/env.cpp +++ b/env.cpp @@ -1,58 +1,83 @@ #include "env.h" namespace basil { - Def::Def(bool is_macro_in, bool is_infix_in, + Def::Def(bool is_macro_in, bool is_procedure_in, bool is_infix_in, u8 arity_in, u8 precedence_in): - Def(Value(), is_macro_in, is_infix_in, arity_in, precedence_in) {} + Def(Value(), is_macro_in, is_procedure_in, is_infix_in, + arity_in, precedence_in) {} - Def::Def(Value value_in, bool is_macro_in, bool is_infix_in, - u8 arity_in, u8 precedence_in): - is_macro(is_macro_in), is_infix(is_infix_in), - arity(arity_in), precedence(precedence_in), value(value_in) {} + Def::Def(Value value_in, bool is_macro_in, bool is_procedure_in, + bool is_infix_in, u8 arity_in, u8 precedence_in): + value(value_in), is_macro(is_macro_in), + is_proc(is_procedure_in), is_infix(is_infix_in), + arity(arity_in), precedence(precedence_in) {} bool Def::is_procedure() const { - return arity > 0 && !is_macro; + return is_proc && !is_macro; } bool Def::is_variable() const { - return arity == 0 && !is_macro; + return !is_proc && !is_macro; } bool Def::is_macro_procedure() const { - return arity > 0 && is_macro; + return is_proc && is_macro; } bool Def::is_macro_variable() const { - return arity == 0 && is_macro; + return !is_proc && is_macro; } Env::Env(const ref& parent): _parent(parent) {} + void Env::def(const string& name) { + _defs[name] = Def(false, false, false); + } + + void Env::def_macro(const string& name) { + _defs[name] = Def(true, false, false); + } + void Env::def(const string& name, u8 arity) { - _defs[name] = Def(false, false, arity); + _defs[name] = Def(false, true, false, arity); } void Env::def_macro(const string& name, u8 arity) { - _defs[name] = Def(true, false, arity); + _defs[name] = Def(true, true, false, arity); } - void Env::infix(const string& name, u8 precedence, u8 arity) { - _defs[name] = Def(false, true, arity, precedence); + void Env::def(const string& name, const Value& value) { + _defs[name] = Def(value, false, false, false); } - void Env::infix_macro(const string& name, u8 precedence, u8 arity) { - _defs[name] = Def(true, true, arity, precedence); + void Env::def(const string& name, const Value& value, u8 arity) { + _defs[name] = Def(value, false, true, false, arity); } - void Env::def(const string& name, const Value& value, u8 arity) { - _defs[name] = Def(value, false, false, arity); // todo: detect arity + void Env::def_macro(const string& name, const Value& value) { + _defs[name] = Def(value, true, false, false); } - - void Env::infix(const string& name, const Value& value, + + void Env::def_macro(const string& name, const Value& value, u8 arity) { + _defs[name] = Def(value, true, true, false, arity); + } + + void Env::infix(const string& name, u8 arity, u8 precedence) { + _defs[name] = Def(false, true, true, arity, precedence); + } + + void Env::infix(const string& name, const Value& value, u8 arity, u8 precedence) { + _defs[name] = Def(value, false, true, true, arity, precedence); + } + + void Env::infix_macro(const string& name, u8 arity, u8 precedence) { + _defs[name] = Def(true, true, true, arity, precedence); + } + + void Env::infix_macro(const string& name, const Value& value, u8 arity, u8 precedence) { - _defs[name] = Def(value, false, true, arity, precedence); - // todo: detect arity + _defs[name] = Def(value, true, true, true, arity, precedence); } const Def* Env::find(const string& name) const { @@ -68,4 +93,20 @@ namespace basil { return _parent ? _parent->find(name) : nullptr; else return &it->second; } + + void Env::format(stream& io) const { + write(io, "{"); + bool first = true; + for (auto& def : _defs) + write(io, first ? "" : " ", def.first), first = false; + write(io, "}"); + if (_parent) write(io, " -> "), _parent->format(io); + } + + ref Env::clone() const { + Env new_env(_parent); + ref new_ref(new_env); + for (auto& p : _defs) new_ref->_defs.put(p.first, p.second); + return new_env; + } } \ No newline at end of file diff --git a/env.h b/env.h index c6fe5a3..4c944c4 100644 --- a/env.h +++ b/env.h @@ -9,16 +9,18 @@ namespace basil { struct Def { + Value value; bool is_macro; // is the definition a macro alias or procedure? bool is_infix; // is the definition for an infix procedure? - u8 arity; // 0 is a variable or alias, 1 or higher is procedure. + bool is_proc; // is the definition a scalar or procedure? + u8 arity; // number of arguments taken by a procedure. u8 precedence; // precedence of infix procedure - Value value; - Def(bool is_macro_in = false, bool is_infix_in = false, - u8 arity_in = 0, u8 precedence_in = 0); - Def(Value value_in, bool is_macro_in = false, + Def(bool is_macro_in = false, bool is_procedure_in = false, bool is_infix_in = false, u8 arity_in = 0, u8 precedence_in = 0); + Def(Value value_in, bool is_procedure_in = false, + bool is_macro_in = false, bool is_infix_in = false, + u8 arity_in = 0, u8 precedence_in = 0); bool is_procedure() const; bool is_variable() const; bool is_macro_procedure() const; @@ -31,16 +33,22 @@ namespace basil { public: Env(const ref& parent = ref::null()); - void def(const string& name, u8 arity = 0); - void def_macro(const string& name, u8 arity = 0); - void infix(const string& name, u8 precedence, u8 arity = 2); - void infix_macro(const string& name, u8 precedence, u8 arity = 2); - void def(const string& name, const Value& value, u8 arity = 0); - void infix(const string& name, const Value& value, u8 arity = 2, - u8 precedence = 0); - bool lookup(const string& name) const; + void def(const string& name); + void def(const string& name, u8 arity); + void def_macro(const string& name); + void def_macro(const string& name, u8 arity); + void def(const string& name, const Value& value); + void def(const string& name, const Value& value, u8 arity); + void def_macro(const string& name, const Value& value); + void def_macro(const string& name, const Value& value, u8 arity); + void infix(const string& name, u8 arity, u8 precedence); + void infix(const string& name, const Value& value, u8 arity, u8 precedence); + void infix_macro(const string& name, u8 arity, u8 precedence); + void infix_macro(const string& name, const Value& value, u8 arity, u8 precedence); const Def* find(const string& name) const; Def* find(const string& name); + void format(stream& io) const; + ref clone() const; }; } diff --git a/eval.cpp b/eval.cpp index 4240aad..63e5554 100644 --- a/eval.cpp +++ b/eval.cpp @@ -21,6 +21,46 @@ namespace basil { return rem(args.get_product()[0], args.get_product()[1]); } + Value builtin_and(ref env, const Value& args) { + return logical_and(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_or(ref env, const Value& args) { + return logical_or(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_xor(ref env, const Value& args) { + return logical_xor(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_not(ref env, const Value& args) { + return logical_not(args.get_product()[0]); + } + + Value builtin_equal(ref env, const Value& args) { + return equal(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_inequal(ref env, const Value& args) { + return inequal(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_less(ref env, const Value& args) { + return less(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_greater(ref env, const Value& args) { + return greater(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_less_equal(ref env, const Value& args) { + return less_equal(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_greater_equal(ref env, const Value& args) { + return greater_equal(args.get_product()[0], args.get_product()[1]); + } + Value builtin_head(ref env, const Value& args) { return head(args.get_product()[0]); } @@ -36,15 +76,27 @@ namespace basil { ref create_root_env() { ref root; root->def("nil", Value(VOID)); - root->def("+", Value(new FunctionValue(root, builtin_add)), 2); - root->def("-", Value(new FunctionValue(root, builtin_sub)), 2); - root->def("*", Value(new FunctionValue(root, builtin_mul)), 2); - root->def("/", Value(new FunctionValue(root, builtin_div)), 2); - root->def("%", Value(new FunctionValue(root, builtin_rem)), 2); - root->def("head", Value(new FunctionValue(root, builtin_head)), 1); - root->def("tail", Value(new FunctionValue(root, builtin_tail)), 1); - root->def("::", Value(new FunctionValue(root, builtin_cons)), 2); - root->def("cons", Value(new FunctionValue(root, builtin_cons)), 2); + root->infix("+", Value(new FunctionValue(root, builtin_add, 2)), 2, 20); + root->infix("-", Value(new FunctionValue(root, builtin_sub, 2)), 2, 20); + root->infix("*", Value(new FunctionValue(root, builtin_mul, 2)), 2, 40); + root->infix("/", Value(new FunctionValue(root, builtin_div, 2)), 2, 40); + root->infix("%", Value(new FunctionValue(root, builtin_rem, 2)), 2, 40); + root->infix("and", Value(new FunctionValue(root, builtin_and, 2)), 2, 5); + root->infix("or", Value(new FunctionValue(root, builtin_or, 2)), 2, 5); + root->infix("xor", Value(new FunctionValue(root, builtin_xor, 2)), 2, 5); + root->def("not", Value(new FunctionValue(root, builtin_not, 1)), 1); + root->infix("==", Value(new FunctionValue(root, builtin_equal, 2)), 2, 10); + root->infix("!=", Value(new FunctionValue(root, builtin_inequal, 2)), 2, 10); + root->infix("<", Value(new FunctionValue(root, builtin_less, 2)), 2, 10); + root->infix(">", Value(new FunctionValue(root, builtin_greater, 2)), 2, 10); + root->infix("<=", Value(new FunctionValue(root, builtin_less_equal, 2)), 2, 10); + root->infix(">=", Value(new FunctionValue(root, builtin_greater_equal, 2)), 2, 10); + root->infix("head", Value(new FunctionValue(root, builtin_head, 1)), 1, 80); + root->infix("tail", Value(new FunctionValue(root, builtin_tail, 1)), 1, 80); + root->infix("::", Value(new FunctionValue(root, builtin_cons, 2)), 2, 15); + root->infix("cons", Value(new FunctionValue(root, builtin_cons, 2)), 2, 15); + root->def("true", Value(true, BOOL)); + root->def("false", Value(false, BOOL)); return root; } @@ -65,22 +117,59 @@ namespace basil { return values; } - void traverse_list(ref env, const Value& list, - void(*fn)(ref, const Value&)) { + vector to_ptr_vector(const Value& list) { + vector values; const Value* v = &list; while (v->is_list()) { - fn(env, v->get_list().head()); + values.push(&v->get_list().head()); v = &v->get_list().tail(); } + return values; } - void traverse_list(ref env, Value& list, - void(*fn)(ref, Value&)) { + vector to_ptr_vector(Value& list) { + vector values; Value* v = &list; while (v->is_list()) { - fn(env, v->get_list().head()); + values.push(&v->get_list().head()); v = &v->get_list().tail(); } + return values; + } + + bool introduces_env(const Value& list) { + if (!list.is_list()) return false; + const Value& h = head(list); + if (!h.is_symbol()) return false; + const string& name = symbol_for(h.get_symbol()); + if (name == "def" || name == "macro") { + const Value& second = tail(list); + if (second.is_list()) { + const Value& third = tail(second); + + // if list has more than three elements and third parameter + // is a list (aka is a procedure-type definition) + if (third.is_list() && tail(third).is_list() && + !head(third).is_symbol()) return true; + } + } + else if (name == "lambda") return true; + + return false; + } + + void traverse_list(ref env, const Value& list, + void(*fn)(ref, const Value&)) { + vector vals = to_ptr_vector(list); + u32 length = introduces_env(list) ? vals.size() - 1 : vals.size(); + for (u32 i = 0; i < length; i ++) fn(env, *vals[i]); + } + + void traverse_list(ref env, Value& list, + void(*fn)(ref, Value&)) { + vector vals = to_ptr_vector(list); + u32 length = introduces_env(list) ? vals.size() - 1 : vals.size(); + for (u32 i = 0; i < length; i ++) fn(env, *vals[i]); } void handle_splice(ref env, Value& item) { @@ -91,82 +180,388 @@ namespace basil { if (t.is_void()) item = Value(VOID); else item = eval(env, head(tail(item))); } + else traverse_list(env, item, handle_splice); + } + + void visit_macro_defs(ref env, const Value& item) { + if (!item.is_list()) return; + Value h = head(item); + if (h.is_symbol() && h.get_symbol() == symbol_value("macro")) { + vector values = to_vector(item); + if (values.size() < 2 || !values[1].is_symbol()) + err(item.loc(), "Expected name in definition."); + if (values.size() < 3) + err(item.loc(), "Expected value in definition."); + if (values.size() > 3) { // procedure + env->def_macro(symbol_for(values[1].get_symbol()), + to_vector(values[2]).size()); + } + else env->def_macro(symbol_for(values[1].get_symbol())); + } + else traverse_list(env, item, visit_macro_defs); + } + + void visit_defs(ref env, const Value& item) { + if (!item.is_list()) return; + Value h = head(item); + if (h.is_symbol() && h.get_symbol() == symbol_value("def")) { + vector values = to_vector(item); + if (values.size() < 2 || !values[1].is_symbol()) + err(item.loc(), "Expected name in definition."); + if (values.size() < 3) + err(item.loc(), "Expected value in definition."); + if (values.size() > 3) { // procedure + env->def(symbol_for(values[1].get_symbol()), + to_vector(values[2]).size()); + } + else env->def(symbol_for(values[1].get_symbol())); + } + else traverse_list(env, item, visit_defs); + } + + void handle_macro(ref env, Value& item); + + Value expand(const Value& macro, const Value& arg) { + if (!macro.is_macro() && !macro.is_error()) { + err(macro.loc(), "Expanded value is not a macro."); + return error(); + } + if (!arg.is_product() && !arg.is_error()) { + err(arg.loc(), "Arguments not provided as a product."); + return error(); + } + if (macro.is_error() || arg.is_error()) return error(); + + const MacroValue& fn = macro.get_macro(); + if (fn.is_builtin()) { + return fn.get_builtin()(fn.get_env(), arg); + } + else { + ref env = fn.get_env(); + u32 argc = arg.get_product().size(), arity = fn.args().size(); + if (argc != arity) { + err(macro.loc(), "Procedure requires ", arity, " arguments, ", + argc, " provided."); + return error(); + } + for (u32 i = 0; i < arity; i ++) { + const string& argname = symbol_for(fn.args()[i]); + env->find(argname)->value = arg.get_product()[i]; + } + Value result = fn.body().clone(); + handle_splice(fn.get_env(), result); + return result; + } + } + + void handle_macro(ref env, Value& item) { + if (item.is_symbol()) { + const Def* def = env->find(symbol_for(item.get_symbol())); + if (def && def->is_macro_variable()) + item = def->value.get_alias().value(); + } + else if (item.is_list()) { + const Value& h = head(item); + if (h.is_symbol()) { + const Def* def = env->find(symbol_for(h.get_symbol())); + if (def && def->is_macro_procedure()) + item = eval_list(env, item); + } + } + } + + Value apply_op(const Value& op, const Value& lhs) { + return list_of(op, lhs); + } + + Value apply_op(const Value& op, const Value& lhs, const Value& rhs) { + return list_of(op, lhs, rhs); + } + + pair infix_helper(ref env, const Value& lhs, + const Value& op, const Def* def, const Value& term) { + Value iter = term; + + if (iter.is_void()) { + err(term.loc(), "Expected term in binary expression."); + return { error(), term }; + } + Value rhs = head(iter); + iter = tail(iter); // consume second term + + if (iter.is_void()) return { apply_op(op, lhs, rhs), iter }; + Value next_op = head(iter); + if (!next_op.is_symbol()) return { apply_op(op, lhs, rhs), iter }; + const Def* next_def = env->find(symbol_for(next_op.get_symbol())); + if (!next_def || !next_def->is_infix) return { apply_op(op, lhs, rhs), iter }; + iter = tail(iter); // consume op + + if (next_def->precedence > def->precedence) { + auto p = infix_helper(env, rhs, next_op, next_def, iter); + return { apply_op(op, lhs, p.first), p.second }; + } + else return infix_helper(env, apply_op(op, lhs, rhs), next_op, next_def, iter); + } + + pair infix_transform(ref env, const Value& term) { + Value iter = term; + + if (iter.is_void()) { + err(term.loc(), "Expected term in binary expression."); + return { error(), term }; + } + Value lhs = head(iter); // 1 + 2 -> 1 + iter = tail(iter); // consume first term + + if (iter.is_void()) return { lhs, iter }; + Value op = head(iter); + if (!op.is_symbol()) return { lhs, iter }; + const Def* def = env->find(symbol_for(op.get_symbol())); + if (!def || !def->is_infix) return { lhs, iter }; + iter = tail(iter); // consume op + + // todo: unary op + + return infix_helper(env, lhs, op, def, iter); + } + + Value handle_infix(ref env, const Value& term) { + vector infix_exprs; + Value iter = term; + while (iter.is_list()) { + auto p = infix_transform(env, iter); + infix_exprs.push(p.first); // next s-expr + iter = p.second; // move past it in source list + } + Value result = list_of(infix_exprs); + return result; } // definition stuff + Value define(ref env, const Value& term, bool is_macro) { + vector values = to_vector(term); + if (values.size() == 3) { // variable + if (is_macro) + env->def_macro(symbol_for(values[1].get_symbol()), + Value(new AliasValue(values[2]))); + else + env->def(symbol_for(values[1].get_symbol()), + eval(env, values[2])); + return Value(VOID); + } + else { + if (!values[2].is_list()) { + err(values[2].loc(), "Expected argument list in function."); + return error(); + } + Env env_descendant(env); + ref function_env(env_descendant); + vector args = to_vector(values[2]); + vector argnames; + for (const Value& v : args) { + if (v.is_symbol()) { + argnames.push(v.get_symbol()); + function_env->def(symbol_for(v.get_symbol())); + } + } + vector body; + for (u32 i = 3; i < values.size(); i ++) body.push(values[i]); + if (is_macro) { + Value mac(new MacroValue(function_env, + argnames, cons(Value("do"), list_of(body)))); + env->def_macro(symbol_for(values[1].get_symbol()), + mac, argnames.size()); + } + else { + Value func(new FunctionValue(function_env, + argnames, cons(Value("do"), handle_infix(env, list_of(body))))); + env->def(symbol_for(values[1].get_symbol()), + func, argnames.size()); + } + return Value(VOID); + } + } + + Value infix(ref env, const Value& term) { + vector values = to_vector(term); + + u8 precedence = 0; + + u32 i = 1; + if (values[i].is_int()) { // precedence + precedence = (u8)values[i].get_int(); + i ++; + } + if (!values[i].is_symbol()) { + err(values[i].loc(), "Expected name for infix definition."); + return error(); + } + const string& name = symbol_for(values[i].get_symbol()); + i ++; + + if (i + 1 >= values.size()) { + err(values[0].loc(), "Expected argument list and body in ", + "infix definition."); + return error(); + } + if (!values[i].is_list()) { + err(values[i].loc(), "Expected argument list in infix ", + "definition."); + return error(); + } + Env env_descendant(env); + ref function_env(env_descendant); + vector args = to_vector(values[i]); + i ++; + vector argnames; + for (const Value& v : args) { + if (v.is_symbol()) { + argnames.push(v.get_symbol()); + function_env->def(symbol_for(v.get_symbol())); + } + } + vector body; + for (; i < values.size(); i ++) body.push(values[i]); + Value func(new FunctionValue(function_env, + argnames, cons(Value("do"), handle_infix(env, list_of(body))))); + env->infix(name, func, argnames.size(), precedence); + return Value(VOID); + } + + Value lambda(ref env, const Value& term) { + vector values = to_vector(term); + if (values.size() < 2) { + err(values[0].loc(), "Expected argument list and body in ", + "lambda expression."); + return error(); + } + if (!values[1].is_list()) { + err(values[1].loc(), "Expected argument list in lambda ", + "expression."); + return error(); + } + Env env_descendant(env); + ref function_env(env_descendant); + vector args = to_vector(values[1]); + vector argnames; + for (const Value& v : args) { + if (v.is_symbol()) { + argnames.push(v.get_symbol()); + function_env->def(symbol_for(v.get_symbol())); + } + } + vector body; + for (u32 i = 2; i < values.size(); i ++) body.push(values[i]); + return Value(new FunctionValue(function_env, argnames, + cons(Value("do"), handle_infix(env, list_of(body))))); + } + + Value do_block(ref env, const Value& term) { + const Value& todo = tail(term); + + const Value* v = &todo; + while (v->is_list()) { + if (v->get_list().tail().is_void()) // last element + return eval(env, v->get_list().head()); + eval(env, v->get_list().head()); + v = &v->get_list().tail(); + } + return Value(VOID); + } + + Value if_expr(ref env, const Value& term) { + Value handled = cons(head(term), handle_infix(env, tail(term))); + vector values = to_vector(handled); + if (values.size() != 4) { + err(term.loc(), "Incorrect number of arguments for if expression. ", + "Expected condition, case if true, and case if false."); + return error(); + } + Value cond = eval(env, values[1]); + if (!cond.is_bool()) { + err(cond.loc(), "Expected boolean condition in if expression, given '", + cond.type(), "'."); + return error(); + } + if (cond.get_bool()) return eval(env, values[2]); + else return eval(env, values[3]); + } + Value eval_list(ref env, const Value& term) { Value h = head(term); if (h.is_symbol()) { const string& name = symbol_for(h.get_symbol()); - if (name == "quote") { - return head(tail(term)); - } - else if (name == "def") { - vector values = to_vector(term); - if (values.size() < 2 || !values[1].is_symbol()) { - err(term.loc(), "Expected name in definition."); - return error(); - } - if (values.size() < 3) { - err(term.loc(), "Expected value in definition."); - return error(); - } - if (values.size() == 3) { // variable - env->def(symbol_for(values[1].get_symbol()), - eval(env, values[2])); - return Value(VOID); - } - else { - if (!values[2].is_list()) { - err(values[2].loc(), "Expected argument list in function."); - return error(); - } - vector args = to_vector(values[2]); - vector argnames; - for (Value v : args) - if (v.is_symbol()) argnames.push(v.get_symbol()); - ref function_env(env); - Value func(new FunctionValue(function_env, - argnames, values[3])); - env->def(symbol_for(values[1].get_symbol()), - func, args.size()); - return Value(VOID); - } + if (name == "quote") return head(tail(term)); + else if (name == "def") return define(env, term, false); + else if (name == "infix") return infix(env, term); + else if (name == "macro") return define(env, term, true); + else if (name == "lambda") return lambda(env, term); + else if (name == "do") return do_block(env, term); + else if (name == "if") return if_expr(env, term); + } + + Value first = eval(env, h); + if (first.is_macro()) { + vector args; + const Value* v = &term.get_list().tail(); + while (v->is_list()) { + args.push(v->get_list().head()); + v = &v->get_list().tail(); } - else if (env->find(name) && env->find(name)->is_procedure()) { - vector args; - const Value* v = &term.get_list().tail(); - while (v->is_list()) { - args.push(eval(env, v->get_list().head())); - v = &v->get_list().tail(); - } - if (args.size() != env->find(name)->arity) { - err(term.loc(), "Procedure expects ", env->find(name)->arity, - " arguments, ", args.size(), " provided."); - return error(); - } - return call(eval(env, h), Value(new ProductValue(args))); + if (args.size() != first.get_macro().arity()) { + err(term.loc(), "Macro procedure expects ", + first.get_macro().arity(), " arguments, ", + args.size(), " provided."); + return error(); } + return expand(first, Value(new ProductValue(args))); } - err(term.loc(), "Could not evaluate list."); - return error(); + else if (first.is_function()) { + vector args; + Value args_term = handle_infix(env, tail(term)); + const Value* v = &args_term; + while (v->is_list()) { + args.push(eval(env, v->get_list().head())); + v = &v->get_list().tail(); + } + if (args.size() != first.get_function().arity()) { + err(term.loc(), "Procedure expects ", + first.get_function().arity(), " arguments, ", + args.size(), " provided."); + return error(); + } + return call(first, Value(new ProductValue(args))); + } + + if (tail(term).is_void()) return eval(env, h); + + Value infix_term = handle_infix(env, term); + if (infix_term == term) { + err(term.loc(), "Could not evaluate list."); + return error(); + } + else return eval_list(env, infix_term); } Value eval(ref env, Value term) { - if (term.is_list()) { - traverse_list(env, term, handle_splice); - return eval_list(env, term); - } + handle_splice(env, term); + visit_macro_defs(env, term); + handle_macro(env, term); + visit_defs(env, term); + if (term.is_list()) return eval_list(env, term); else if (term.is_int()) return term; else if (term.is_symbol()) { const string& name = symbol_for(term.get_symbol()); const Def* def = env->find(name); - if (def) return def->value; + if (def && def->is_macro_variable()) + return def->value.get_alias().value(); + else if (def) return def->value; else { err(term.loc(), "Undefined variable '", name, "'."); return error(); } } + return error(); } } \ No newline at end of file diff --git a/lex.cpp b/lex.cpp index b85aafa..7a35856 100644 --- a/lex.cpp +++ b/lex.cpp @@ -101,10 +101,11 @@ namespace basil { } else if (isdigit(ch)) { while (isdigit(view.peek())) view.read(); - if (isalpha(view.peek())) - return Token(T_COEFF, { u32(view.pos() - start), start }, line, start_col); + if (issymbolstart(view.peek()) || view.peek() == '(' || view.peek() == '[' + || view.peek() == '{') + return Token(T_COEFF, { u32(view.pos() - start), start }, line, start_col); else if (isdelimiter(view.peek()) || isspace(view.peek())) - return Token(T_INT, { u32(view.pos() - start), start }, line, start_col); + return Token(T_INT, { u32(view.pos() - start), start }, line, start_col); else err({ line, u16(view.col()) }, "Unexpected character in integer '", view.peek(), "'."); } @@ -130,6 +131,10 @@ namespace basil { return t; } + void TokenView::rewind() { + if (i) i --; + } + TokenView::operator bool() const { return i < _tokens->size(); } diff --git a/lex.h b/lex.h index 2db9eab..442223b 100644 --- a/lex.h +++ b/lex.h @@ -35,6 +35,7 @@ namespace basil { const Token& peek(); const Token& read(); + void rewind(); operator bool() const; bool repl() const; void expand(); diff --git a/main b/main index a06624decfa860096e49f77d291e111ada1ba5a6..f970ffe3a7e0a99a136c29fafc20420b70c4d773 100644 GIT binary patch literal 245824 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̈́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
eL^#w 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%0DXz^m~= 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 diff --git a/main.cpp b/main.cpp index d11b90e..a5c761a 100644 --- a/main.cpp +++ b/main.cpp @@ -4,28 +4,12 @@ #include "parse.h" #include "eval.h" -// ubuntu and mac color codes, from -// https://stackoverflow.com/questions/9158150/colored-output-in-c/ -#define RESET "\033[0m" -#define BLACK "\033[30m" /* Black */ -#define RED "\033[31m" /* Red */ -#define GREEN "\033[32m" /* Green */ -#define YELLOW "\033[33m" /* Yellow */ -#define BLUE "\033[34m" /* Blue */ -#define MAGENTA "\033[35m" /* Magenta */ -#define CYAN "\033[36m" /* Cyan */ -#define WHITE "\033[37m" /* White */ -#define BOLDBLACK "\033[1m\033[30m" /* Bold Black */ -#define BOLDRED "\033[1m\033[31m" /* Bold Red */ -#define BOLDGREEN "\033[1m\033[32m" /* Bold Green */ -#define BOLDYELLOW "\033[1m\033[33m" /* Bold Yellow */ -#define BOLDBLUE "\033[1m\033[34m" /* Bold Blue */ -#define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */ -#define BOLDCYAN "\033[1m\033[36m" /* Bold Cyan */ -#define BOLDWHITE "\033[1m\033[37m" /* Bold White */ - using namespace basil; +#define PRINT_TOKENS false +#define PRINT_AST false +#define PRINT_EVAL true + int main() { Source repl; @@ -41,57 +25,38 @@ int main() { tokens.push(scan(view)); if (error_count()) { print_errors(_stdout, repl), clear_errors(); - return 1; + clear_errors(); + continue; + } + else if (PRINT_TOKENS) { + print(BOLDYELLOW, "⬤ "); + for (const Token& t : tokens) print(t, " "); + println(RESET); } vector lines; TokenView tview(tokens, repl, true); - while (tview.peek()) - lines.push(parse_line(tview, tview.peek().column)); + while (tview.peek()) { + Value line = parse_line(tview, tview.peek().column); + if (!line.is_void()) lines.push(line); + } if (error_count()) { print_errors(_stdout, repl), clear_errors(); - return 1; + clear_errors(); + continue; } + else if (PRINT_AST) for (Value v : lines) + println(BOLDGREEN, "∧ ", v, RESET); vector results; for (Value v : lines) results.push(eval(global, v)); if (error_count()) { print_errors(_stdout, repl), clear_errors(); - return 1; + clear_errors(); + continue; } - else println(BOLDBLUE, "= ", results.back(), RESET, "\n"); + else if (PRINT_EVAL) + println(BOLDBLUE, "= ", results.back(), RESET, "\n"); } -} - -// Value append(Value a, Value b) { -// if (a.is_void()) return b; -// return cons(head(a), append(tail(a), b)); -// } - -// Value reverse(Value a, Value acc) { -// if (a.is_void()) return acc; -// else return reverse(tail(a), cons(head(a), acc)); -// } - -// Value reverse(Value a) { -// return ::reverse(a, empty()); -// } - - // Source src("tests/example.bl"); - - // Source::View v = src.begin(); - // while (v.peek()) { - // Token t = scan(v); - // print(t, t.type == T_NEWLINE ? "\n" : " "); - // } - - // Source repl; - - // while (true) { - // Source::View view = repl.expand(_stdin); - // while (view.peek()) - // println(scan(view)); - // if (error_count()) - // print_errors(_stdout, repl), clear_errors(); - // } \ No newline at end of file +} \ No newline at end of file diff --git a/parse.cpp b/parse.cpp index 2e7ccd7..0318949 100644 --- a/parse.cpp +++ b/parse.cpp @@ -60,7 +60,7 @@ namespace basil { Value apply_op(TokenView& view, Value lhs, Value rhs, TokenType op) { switch (op) { case T_COLON: { - Value v = list_of(string("annotate"), lhs, rhs); + Value v = list_of(Value("annotate"), lhs, rhs); v.set_location(lhs.loc()); return v; } @@ -125,6 +125,12 @@ namespace basil { v.set_location(first); return v; } + case T_COEFF: { + i64 i = parse_int(view.read().value); + Value v = list_of(Value("*"), i, parse_primary(view, indent)); + v.set_location(first); + return v; + } case T_LPAREN: { view.read(); vector terms; @@ -174,7 +180,8 @@ namespace basil { view.read(), v = parse_binary(view, v, T_DOT, indent); if (view.peek().type == T_COLON) { view.read(); - if (view.peek().type == T_NEWLINE) { + // parse a labeled block + if (v.is_symbol() && view.peek().type == T_NEWLINE) { view.read(); vector terms; terms.push(v); // label; @@ -183,6 +190,10 @@ namespace basil { parse_block(view, terms, indent, view.peek().column); return list_of(terms); } + else if (view.peek().type == T_NEWLINE) { + view.rewind(); // unget : + return v; // unlabeled block, handled in parse_line + } else v = parse_binary(view, v, T_COLON, indent); } return v; @@ -192,12 +203,26 @@ namespace basil { SourceLocation first = view.peek(); vector terms; while (view.peek() && view.peek().type != T_NEWLINE) { + if (view.peek().type == T_COLON) { + view.read(); + if (view.peek().type == T_NEWLINE) { + view.read(); + if (!view.peek() && out_of_input(view)) return error(); + if (view.peek().column > indent) + parse_block(view, terms, indent, view.peek().column); + return list_of(terms); + } + else { + err(view.peek(), "Unexpected colon in input."); + return error(); + } + } Value v = parse(view, indent); if (v.is_error()) return error(); else terms.push(v); } if (consume_line) view.read(); - Value v = terms.size() == 1 ? terms[0] : list_of(terms); + Value v = list_of(terms); v.set_location(first); return v; } diff --git a/rc.h b/rc.h index a1bed81..848bdd2 100644 --- a/rc.h +++ b/rc.h @@ -18,20 +18,17 @@ template class ref { struct BoxedValue { u64 count; - T* data; + T data; }* _value; ref(NullType): _value(nullptr) {} public: static constexpr ref null(); template - ref(Args... args): _value(new BoxedValue{0, new T(args...)}) {} + ref(Args... args): _value(new BoxedValue{1, T(args...)}) {} ~ref() { - if (_value && !--_value->count) { - delete _value->data; - delete _value; - } + if (_value && !--_value->count) delete _value; } ref(const ref& other): _value(other._value) { @@ -40,27 +37,24 @@ class ref { ref& operator=(const ref& other) { if (other._value) other._value->count ++; - if (_value && !--_value->count) { - delete _value->data; - delete _value; - } + if (_value && !--_value->count) delete _value; _value = other._value; } const T& operator*() const { - return *_value->data; + return _value->data; } T& operator*() { - return *_value->data; + return _value->data; } const T* operator->() const { - return _value->data; + return &_value->data; } T* operator->() { - return _value->data; + return &_value->data; } operator bool() const { diff --git a/type.cpp b/type.cpp index 3f161af..05b32f7 100644 --- a/type.cpp +++ b/type.cpp @@ -148,6 +148,41 @@ namespace basil { write(io, "(", _arg, " -> ", _ret, ")"); } + AliasType::AliasType(): + Type(9323462044786133851ul) {} + + TypeKind AliasType::kind() const { + return KIND_ALIAS; + } + + bool AliasType::operator==(const Type& other) const { + return other.kind() == kind(); + } + + void AliasType::format(stream& io) const { + write(io, "alias"); + } + + MacroType::MacroType(u32 arity): + Type(18254210403858406693ul ^ ::hash(arity)), _arity(arity) {} + + u32 MacroType::arity() const { + return _arity; + } + + TypeKind MacroType::kind() const { + return KIND_MACRO; + } + + bool MacroType::operator==(const Type& other) const { + return other.kind() == kind() && + ((const MacroType&)other).arity() == arity(); + } + + void MacroType::format(stream& io) const { + write(io, "macro(", _arity, ")"); + } + struct TypeBox { const Type* t; @@ -173,7 +208,9 @@ namespace basil { *SYMBOL = find("symbol"), *VOID = find("void"), *ERROR = find("error"), - *TYPE = find("type"); + *TYPE = find("type"), + *ALIAS = find(), + *BOOL = find("bool"); } template<> diff --git a/type.h b/type.h index c0ed17b..471e8a7 100644 --- a/type.h +++ b/type.h @@ -15,7 +15,9 @@ namespace basil { KIND_LIST = GC_KIND_FLAG | 0, KIND_SUM = GC_KIND_FLAG | 1, KIND_PRODUCT = GC_KIND_FLAG | 2, - KIND_FUNCTION = GC_KIND_FLAG | 3 + KIND_FUNCTION = GC_KIND_FLAG | 3, + KIND_ALIAS = GC_KIND_FLAG | 4, + KIND_MACRO = GC_KIND_FLAG | 5 }; class Type { @@ -87,6 +89,27 @@ namespace basil { void format(stream& io) const override; }; + class AliasType : public Type { + public: + AliasType(); + + TypeKind kind() const override; + bool operator==(const Type& other) const override; + void format(stream& io) const override; + }; + + class MacroType : public Type { + u32 _arity; + public: + MacroType(u32 arity); + + u32 arity() const; + + TypeKind kind() const override; + bool operator==(const Type& other) const override; + void format(stream& io) const override; + }; + const Type* find_existing_type(const Type* t); const Type* create_type(const Type* t); @@ -99,7 +122,8 @@ namespace basil { return create_type(new T(t)); } - extern const Type *INT, *SYMBOL, *VOID, *ERROR, *TYPE; + extern const Type *INT, *SYMBOL, *VOID, *ERROR, *TYPE, + *ALIAS, *BOOL; } template<> diff --git a/values.cpp b/values.cpp index e1382fb..f53336d 100644 --- a/values.cpp +++ b/values.cpp @@ -29,7 +29,7 @@ namespace basil { Value::Value(i64 i, const Type* type): _type(type) { - _data.i = i; + is_bool() ? _data.b = i : _data.i = i; } Value::Value(const string& s, const Type* type): @@ -64,6 +64,16 @@ namespace basil { _data.rc = f; } + Value::Value(AliasValue* a): + _type(ALIAS) { + _data.rc = a; + } + + Value::Value(MacroValue* m): + _type(find(m->arity())) { + _data.rc = m; + } + Value::~Value() { if (_type->kind() & GC_KIND_FLAG) _data.rc->dec(); } @@ -127,6 +137,18 @@ namespace basil { return _data.t; } + bool Value::is_bool() const { + return _type == BOOL; + } + + bool Value::get_bool() const { + return _data.b; + } + + bool& Value::get_bool() { + return _data.b; + } + bool Value::is_list() const { return _type->kind() == KIND_LIST; } @@ -175,6 +197,30 @@ namespace basil { return *(FunctionValue*)_data.rc; } + bool Value::is_alias() const { + return _type->kind() == KIND_ALIAS; + } + + const AliasValue& Value::get_alias() const { + return *(const AliasValue*)_data.rc; + } + + AliasValue& Value::get_alias() { + return *(AliasValue*)_data.rc; + } + + bool Value::is_macro() const { + return _type->kind() == KIND_MACRO; + } + + const MacroValue& Value::get_macro() const { + return *(const MacroValue*)_data.rc; + } + + MacroValue& Value::get_macro() { + return *(MacroValue*)_data.rc; + } + const Type* Value::type() const { return _type; } @@ -185,6 +231,7 @@ namespace basil { else if (is_int()) write(io, get_int()); else if (is_symbol()) write(io, symbol_for(get_symbol())); else if (is_type()) write(io, get_type()); + else if (is_bool()) write(io, get_bool()); else if (is_list()) { bool first = true; write(io, "("); @@ -200,17 +247,15 @@ namespace basil { else if (is_product()) { bool first = true; write(io, "("); - const Value* ptr = this; - while (ptr->is_list()) { - write(io, first ? "" : ", ", ptr->get_list().head()); - ptr = &ptr->get_list().tail(); + for (const Value& v : get_product()) { + write(io, first ? "" : ", ", v); first = false; } write(io, ")"); } - else if (is_function()) { - write(io, ""); - } + else if (is_function()) write(io, ""); + else if (is_alias()) write(io, ""); + else if (is_macro()) write(io, ""); } u64 Value::hash() const { @@ -219,6 +264,9 @@ namespace basil { else if (is_int()) return ::hash(get_int()) ^ 6909969109598810741ul; else if (is_symbol()) return ::hash(get_symbol()) ^ 1899430078708870091ul; else if (is_type()) return get_type()->hash(); + else if (is_bool()) + return get_bool() ? 9269586835432337327ul + : 18442604092978916717ul; else if (is_list()) { u64 h = 9572917161082946201ul; Value ptr = *this; @@ -228,6 +276,37 @@ namespace basil { } return h; } + else if (is_sum()) { + return get_sum().value().hash() ^ 7458465441398727979ul; + } + else if (is_product()) { + u64 h = 16629385277682082909ul; + for (const Value& v : get_product()) h ^= v.hash(); + return h; + } + else if (is_function()) { + u64 h = 10916307465547805281ul; + if (get_function().is_builtin()) + h ^= ::hash(get_function().get_builtin()); + else { + h ^= get_function().body().hash(); + for (u64 arg : get_function().args()) + h ^= ::hash(arg); + } + return h; + } + else if (is_alias()) return 6860110315984869641ul; + else if (is_macro()) { + u64 h = 16414641732770006573ul; + if (get_macro().is_builtin()) + h ^= ::hash(get_macro().get_builtin()); + else { + h ^= get_macro().body().hash(); + for (u64 arg : get_macro().args()) + h ^= ::hash(arg); + } + return h; + } return 0; } @@ -236,6 +315,7 @@ namespace basil { else if (is_int()) return get_int() == other.get_int(); else if (is_symbol()) return get_symbol() == other.get_symbol(); else if (is_type()) return get_type() == other.get_type(); + else if (is_bool()) return get_bool() == other.get_bool(); else if (is_list()) { const ListValue* l = &get_list(), *o = &other.get_list(); do { @@ -244,9 +324,73 @@ namespace basil { } while (l->tail().is_list() && o->tail().is_list()); return l->head() == o->head() && l->tail().is_void() && o->tail().is_void(); } + else if (is_function()) { + if (get_function().is_builtin()) + return get_function().get_builtin() == + other.get_function().get_builtin(); + else { + if (other.get_function().arity() != get_function().arity()) + return false; + for (u32 i = 0; i < get_function().arity(); i ++) { + if (other.get_function().args()[i] != + get_function().args()[i]) return false; + } + return get_function().body() == other.get_function().body(); + } + } + else if (is_macro()) { + if (get_macro().is_builtin()) + return get_macro().get_builtin() == + other.get_macro().get_builtin(); + else { + if (other.get_macro().arity() != get_macro().arity()) + return false; + for (u32 i = 0; i < get_macro().arity(); i ++) { + if (other.get_macro().args()[i] != + get_macro().args()[i]) return false; + } + return get_macro().body() == other.get_macro().body(); + } + } return type() == other.type(); } + Value Value::clone() const { + if (is_list()) + return Value(new ListValue(get_list().head().clone(), + get_list().tail().clone())); + else if (is_sum()) + return Value(new SumValue(get_sum().value()), type()); + else if (is_product()) { + vector values; + for (const Value& v : get_product()) values.push(v); + return Value(new ProductValue(values)); + } + else if (is_function()) { + if (get_function().is_builtin()) { + return Value(new FunctionValue(get_function().get_env()->clone(), + get_function().get_builtin(), get_function().arity())); + } + else { + return Value(new FunctionValue(get_function().get_env()->clone(), + get_function().args(), get_function().body().clone())); + } + } + else if (is_alias()) + return Value(new AliasValue(get_alias().value())); + else if (is_macro()) { + if (get_macro().is_builtin()) { + return Value(new MacroValue(get_macro().get_env()->clone(), + get_macro().get_builtin(), get_macro().arity())); + } + else { + return Value(new MacroValue(get_macro().get_env()->clone(), + get_macro().args(), get_macro().body().clone())); + } + } + return *this; + } + bool Value::operator!=(const Value& other) const { return !(*this == other); } @@ -324,8 +468,9 @@ namespace basil { const Value& code): _code(code), _builtin(nullptr), _env(env), _args(args) {} - FunctionValue::FunctionValue(ref env, Builtin builtin): - _builtin(builtin), _env(env) {} + FunctionValue::FunctionValue(ref env, BuiltinFn builtin, + u64 arity): + _builtin(builtin), _env(env), _builtin_arity(arity) {} const vector& FunctionValue::args() const { return _args; @@ -335,7 +480,7 @@ namespace basil { return _builtin; } - Builtin FunctionValue::get_builtin() const { + BuiltinFn FunctionValue::get_builtin() const { return _builtin; } @@ -347,10 +492,61 @@ namespace basil { return _env; } + u64 FunctionValue::arity() const { + return _builtin ? _builtin_arity : _args.size(); + } + const Value& FunctionValue::body() const { return _code; } + AliasValue::AliasValue(const Value& value): + _value(value) {} + + Value& AliasValue::value() { + return _value; + } + + const Value& AliasValue::value() const { + return _value; + } + + MacroValue::MacroValue(ref env, const vector& args, + const Value& code): + _code(code), _builtin(nullptr), _env(env), _args(args) {} + + MacroValue::MacroValue(ref env, BuiltinMacro builtin, + u64 arity): + _builtin(builtin), _env(env), _builtin_arity(arity) {} + + const vector& MacroValue::args() const { + return _args; + } + + bool MacroValue::is_builtin() const { + return _builtin; + } + + BuiltinMacro MacroValue::get_builtin() const { + return _builtin; + } + + ref MacroValue::get_env() { + return _env; + } + + const ref MacroValue::get_env() const { + return _env; + } + + u64 MacroValue::arity() const { + return _builtin ? _builtin_arity : _args.size(); + } + + const Value& MacroValue::body() const { + return _code; + } + Value binary_arithmetic(const Value& lhs, const Value& rhs, i64(*op)(i64, i64)) { if (!lhs.is_int() && !lhs.is_error()) { err(lhs.loc(), "Expected integer value in arithmetic expression, found '", @@ -387,6 +583,86 @@ namespace basil { return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a % b; }); } + Value binary_logic(const Value& lhs, const Value& rhs, bool(*op)(bool, bool)) { + if (!lhs.is_bool() && !lhs.is_error()) { + err(lhs.loc(), "Expected boolean value in logical expression, found '", + lhs.type(), "'."); + return error(); + } + if (!rhs.is_bool() && !lhs.is_error()) { + err(rhs.loc(), "Expected boolean value in logical expression, found '", + rhs.type(), "'."); + return error(); + } + if (lhs.is_error() || rhs.is_error()) return error(); + + return Value(op(lhs.get_bool(), rhs.get_bool()), BOOL); + } + + Value logical_and(const Value& lhs, const Value& rhs) { + return binary_logic(lhs, rhs, [](bool a, bool b) -> bool { return a && b; }); + } + + Value logical_or(const Value& lhs, const Value& rhs) { + return binary_logic(lhs, rhs, [](bool a, bool b) -> bool { return a || b; }); + } + + Value logical_xor(const Value& lhs, const Value& rhs) { + return binary_logic(lhs, rhs, [](bool a, bool b) -> bool { return a ^ b; }); + } + + Value logical_not(const Value& v) { + if (!v.is_bool() && !v.is_error()) { + err(v.loc(), "Expected boolean value in logical expression, found '", + v.type(), "'."); + return error(); + } + if (v.is_error()) return error(); + + return Value(!v.get_bool(), BOOL); + } + + Value equal(const Value& lhs, const Value& rhs) { + if (lhs.is_error() || rhs.is_error()) return error(); + return Value(lhs == rhs, BOOL); + } + + Value inequal(const Value& lhs, const Value& rhs) { + return Value(!equal(lhs, rhs).get_bool(), BOOL); + } + + Value binary_relation(const Value& lhs, const Value& rhs, bool(*op)(i64, i64)) { + if (!lhs.is_int() && !lhs.is_error()) { + err(lhs.loc(), "Expected integer value in relational expression, found '", + lhs.type(), "'."); + return error(); + } + if (!rhs.is_int() && !lhs.is_error()) { + err(rhs.loc(), "Expected integer value in relational expression, found '", + rhs.type(), "'."); + return error(); + } + if (lhs.is_error() || rhs.is_error()) return error(); + + return Value(op(lhs.get_int(), rhs.get_int()), BOOL); + } + + Value less(const Value& lhs, const Value& rhs) { + return binary_relation(lhs, rhs, [](i64 a, i64 b) -> bool { return a < b; }); + } + + Value greater(const Value& lhs, const Value& rhs) { + return binary_relation(lhs, rhs, [](i64 a, i64 b) -> bool { return a > b; }); + } + + Value less_equal(const Value& lhs, const Value& rhs) { + return binary_relation(lhs, rhs, [](i64 a, i64 b) -> bool { return a <= b; }); + } + + Value greater_equal(const Value& lhs, const Value& rhs) { + return binary_relation(lhs, rhs, [](i64 a, i64 b) -> bool { return a >= b; }); + } + Value head(const Value& v) { if (!v.is_list() && !v.is_error()) { err(v.loc(), "Can only get head of value of list type, given '", @@ -431,7 +707,7 @@ namespace basil { Value list_of(const vector& elements) { Value l = empty(); - for (i64 i = elements.size() - 1; i >= 0; i --) { + for (i64 i = i64(elements.size()) - 1; i >= 0; i --) { l = cons(elements[i], l); } return l; diff --git a/values.h b/values.h index 36b0daf..f977487 100644 --- a/values.h +++ b/values.h @@ -17,6 +17,8 @@ namespace basil { class SumValue; class ProductValue; class FunctionValue; + class AliasValue; + class MacroValue; class Value { const Type* _type; @@ -24,6 +26,7 @@ namespace basil { i64 i; u64 u; const Type* t; + bool b; RC* rc; } _data; SourceLocation _loc; @@ -37,6 +40,8 @@ namespace basil { Value(SumValue* s, const Type* type); Value(ProductValue* p); Value(FunctionValue* f); + Value(AliasValue* f); + Value(MacroValue* f); ~Value(); Value(const Value& other); Value& operator=(const Value& other); @@ -57,6 +62,10 @@ namespace basil { const Type* get_type() const; const Type*& get_type(); + bool is_bool() const; + bool get_bool() const; + bool& get_bool(); + bool is_list() const; const ListValue& get_list() const; ListValue& get_list(); @@ -73,11 +82,20 @@ namespace basil { const FunctionValue& get_function() const; FunctionValue& get_function(); + bool is_alias() const; + const AliasValue& get_alias() const; + AliasValue& get_alias(); + + bool is_macro() const; + const MacroValue& get_macro() const; + MacroValue& get_macro(); + const Type* type() const; void format(stream& io) const; u64 hash() const; bool operator==(const Value& other) const; bool operator!=(const Value& other) const; + Value clone() const; void set_location(SourceLocation loc); SourceLocation loc() const; @@ -117,32 +135,77 @@ namespace basil { Value* end(); }; - using Builtin = Value (*)(ref, const Value& params); + using BuiltinFn = Value (*)(ref, const Value& params); class FunctionValue : public RC { Value _code; - Builtin _builtin; + BuiltinFn _builtin; ref _env; + u64 _builtin_arity; vector _args; public: FunctionValue(ref env, const vector& args, const Value& code); - FunctionValue(ref env, Builtin builtin); + FunctionValue(ref env, BuiltinFn builtin, u64 arity); const vector& args() const; const Value& body() const; bool is_builtin() const; - Builtin get_builtin() const; + u64 arity() const; + BuiltinFn get_builtin() const; ref get_env(); const ref get_env() const; }; + class AliasValue : public RC { + Value _value; + public: + AliasValue(const Value& value); + + Value& value(); + const Value& value() const; + }; + + using BuiltinMacro = Value (*)(ref, const Value& params); + + class MacroValue : public RC { + Value _code; + BuiltinMacro _builtin; + ref _env; + u64 _builtin_arity; + vector _args; + public: + MacroValue(ref env, const vector& args, + const Value& code); + MacroValue(ref env, BuiltinFn builtin, u64 arity); + + const vector& args() const; + const Value& body() const; + bool is_builtin() const; + u64 arity() const; + BuiltinFn get_builtin() const; + ref get_env(); + const ref get_env() const; + }; + Value add(const Value& lhs, const Value& rhs); Value sub(const Value& lhs, const Value& rhs); Value mul(const Value& lhs, const Value& rhs); Value div(const Value& lhs, const Value& rhs); Value rem(const Value& lhs, const Value& rhs); + Value logical_and(const Value& lhs, const Value& rhs); + Value logical_or(const Value& lhs, const Value& rhs); + Value logical_xor(const Value& lhs, const Value& rhs); + Value logical_not(const Value& v); + + Value equal(const Value& lhs, const Value& rhs); + Value inequal(const Value& lhs, const Value& rhs); + Value less(const Value& lhs, const Value& rhs); + Value greater(const Value& lhs, const Value& rhs); + Value less_equal(const Value& lhs, const Value& rhs); + Value greater_equal(const Value& lhs, const Value& rhs); + Value head(const Value& v); Value tail(const Value& v); Value cons(const Value& head, const Value& tail); From 8404b109e10d9bac6375d0fdc97e807b1944850c Mon Sep 17 00:00:00 2001 From: elucent <9532786+elucent@users.noreply.github.com> Date: Mon, 31 Aug 2020 07:42:21 -0400 Subject: [PATCH 03/17] Towards the end of the jam commit. Lots of stuff works. :) --- .gitignore | 2 +- .replit | 1 + Makefile | 16 +- README.md | 578 +++++++++++++++++ ast.cpp | 796 ++++++++++++++++++++++++ ast.h | 392 ++++++++++++ dev-notes/PROJECT | 43 ++ dev-notes/TODO | 148 +++++ driver.cpp | 218 +++++++ driver.h | 21 + env.cpp | 25 +- env.h | 16 +- errors.cpp | 15 +- errors.h | 10 +- eval.cpp | 833 ++++++++++++++++++++----- eval.h | 5 +- jasmine/obj.cpp | 268 ++++++++ jasmine/obj.h | 55 ++ jasmine/sym.cpp | 42 ++ jasmine/sym.h | 24 + jasmine/target.h | 24 + jasmine/utils.cpp | 117 ++++ jasmine/utils.h | 111 ++++ jasmine/x64.cpp | 1244 +++++++++++++++++++++++++++++++++++++ jasmine/x64.h | 241 +++++++ lex.cpp | 22 +- lex.h | 4 +- main | Bin 245824 -> 0 bytes main.cpp | 950 ++++++++++++++++++++++++++-- native.cpp | 118 ++++ native.h | 14 + parse.cpp | 121 ++-- parse.h | 4 +- source.cpp | 12 +- source.h | 9 +- ssa.cpp | 706 +++++++++++++++++++++ ssa.h | 391 ++++++++++++ std/control.bl | 7 + std/list.bl | 40 ++ type.cpp | 110 +++- type.h | 46 +- util/README.md | 18 + defs.h => util/defs.h | 26 +- hash.cpp => util/hash.cpp | 0 hash.h => util/hash.h | 0 io.cpp => util/io.cpp | 5 +- io.h => util/io.h | 0 rc.cpp => util/rc.cpp | 0 rc.h => util/rc.h | 1 + slice.h => util/slice.h | 0 str.cpp => util/str.cpp | 0 str.h => util/str.h | 0 vec.h => util/vec.h | 0 values.cpp | 451 ++++++++++++-- values.h | 65 +- 55 files changed, 8009 insertions(+), 356 deletions(-) create mode 100644 .replit create mode 100644 README.md create mode 100644 ast.cpp create mode 100644 ast.h create mode 100644 dev-notes/PROJECT create mode 100644 dev-notes/TODO create mode 100644 driver.cpp create mode 100644 driver.h create mode 100644 jasmine/obj.cpp create mode 100644 jasmine/obj.h create mode 100644 jasmine/sym.cpp create mode 100644 jasmine/sym.h create mode 100644 jasmine/target.h create mode 100644 jasmine/utils.cpp create mode 100644 jasmine/utils.h create mode 100644 jasmine/x64.cpp create mode 100644 jasmine/x64.h delete mode 100644 main create mode 100644 native.cpp create mode 100644 native.h create mode 100644 ssa.cpp create mode 100644 ssa.h create mode 100644 std/control.bl create mode 100644 std/list.bl create mode 100644 util/README.md rename defs.h => util/defs.h (75%) rename hash.cpp => util/hash.cpp (100%) rename hash.h => util/hash.h (100%) rename io.cpp => util/io.cpp (98%) rename io.h => util/io.h (100%) rename rc.cpp => util/rc.cpp (100%) rename rc.h => util/rc.h (98%) rename slice.h => util/slice.h (100%) rename str.cpp => util/str.cpp (100%) rename str.h => util/str.h (100%) rename vec.h => util/vec.h (100%) diff --git a/.gitignore b/.gitignore index c82b473..681d0a0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ *.o -basil +basil \ No newline at end of file diff --git a/.replit b/.replit new file mode 100644 index 0000000..2a25d87 --- /dev/null +++ b/.replit @@ -0,0 +1 @@ +run = "make -j basil && ./basil intro" \ No newline at end of file diff --git a/Makefile b/Makefile index 630c8f3..3d5ed57 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,22 @@ -SRCS := $(wildcard *.cpp) +SRCS := $(wildcard *.cpp) $(wildcard util/*.cpp) $(wildcard jasmine/*.cpp) OBJS := $(patsubst %.cpp,%.o,$(SRCS)) CXX := clang++ -CXXFLAGS := -std=c++11 -Os +CXXHEADERS := -I. -Iutil -Ijasmine +CXXFLAGS := $(CXXHEADERS) -std=c++17 -ffast-math -fno-rtti -fno-exceptions -ffunction-sections -Wno-null-dereference clean: - rm -f $(OBJS) basil + rm -f $(OBJS) *.o.tmp basil + +basil: CXXFLAGS += -g3 + +release: CXXFLAGS += -Os basil: $(OBJS) $(CXX) $(CXXFLAGS) $^ -o $@ -%.o: %.cpp +release: $(OBJS) + $(CXX) $(CXXFLAGS) $^ -o basil + +%.o: %.cpp %.h $(CXX) $(CXXFLAGS) -c $< -o $@ diff --git a/README.md b/README.md new file mode 100644 index 0000000..9e22032 --- /dev/null +++ b/README.md @@ -0,0 +1,578 @@ +# The Basil Programming Language - revision 0.1 + +--- + +## Table of Contents + +1. [Project Structure](#language-summary) + +2. [Language Summary](#language-summary) + +3. [Syntax](#syntax) + +4. [Evaluation](#evaluation) + +5. [Types](#types) + +6. [Built-In Procedures](#built-in-procedures) + +7. [Compilation](#compilation) + +--- + +## Project Structure + +| File | Description | +|---|---| +| `jasmine/` | An external library for lightweight machine code generation. Repository [here](https://github.com/elucent/jasmine). | +| `util/` | C++ utilities. Described [here](https://repl.it/@basilTeam/basil#util/README.md). | +| `source.h/cpp` | A traversible and growable representation of a Basil source. | +| `errors.h/cpp` | An error reporting and formatting utility. | +| `lex.h/cpp` | A token type, and the implementation of the Basil tokenizer. | +| `type.h/cpp` | A few kinds of types, that can describe any Basil value. | +| `env.h/cpp` | A definition type for Basil variables, and a lexically-scoped environment in which they can be declared and found. | +| `values.h/cpp` | A dynamic value type, which can represent any Basil value, as well as a number of associated primitive operations. | +| `eval.h/cpp` | Functions and utilities for interpreting Basil values as code. | +| `ast.h/cpp` | A suite of typed AST nodes, for use in generating runtime code. | +| `ssa.h/cpp` | A suite of instruction types, generated from AST nodes and lowerable to x86_64 machine code. | +| `native.h/cpp` | A few native intrinsics, used to implement built-in behaviors such as allocation or IO. | +| `driver.h/cpp` | A few frontend functions for invoking the Basil compiler in different ways. | +| `main.cpp` | The driver function for the Basil command-line application. | + +--- + +## Language Summary + +Basil is a lightweight language with homoiconic syntax, high performance, and +an emphasis on supporting expressive code. Many of its base features are based on +Lisp, and you could probably consider it a Lisp dialect, but it also does a number +of distinctly un-Lispy things. + +All Basil programs are made up of terms. Terms can only be three things: a number, +a string, a symbol, or a list of zero or more terms. All of these terms have valid +Basil values as well, so code can be interpreted as data and data can be interpreted +as code. Homoiconicity! + +Unlike most Lisp dialects, Basil cuts down on parentheses as much as possible, +replacing them with significant whitespace rules and infix operator support. +A program that looks like this in Scheme: +```scheme +(define (factorial x) + (if (= x 0) 1 + (* x (factorial (- x 1))))) +(display (factorial 10)) +``` +...looks like this in Basil: +```rb +infix (x factorial) + if x == 0 1 + :else x - 1 factorial * x +display 10 factorial +``` +Even if you don't think the function itself is much clearer or simpler, you can't +deny that being able to type `display 10 factorial` is pretty cool! + +The real killer feature of Basil, however, is its efficiency. Basil is a _compiled_, +_statically-typed_ language, and it manages to achieve this along with great +expressivity thanks to a pretty novel concept. + +Basically, when the Basil compiler tries to compile a program, it's really +interpreting it. It will automatically compute any expressions it can, and for many +small toy programs the compiler can evaluate them fully - the executable produced +just spits out a constant. + +But the compiler is still a compiler, it doesn't touch any code that might have +consequences. So anything with side-effects, such as IO functions, mutation of +variables, or potentially-non-terminating code gets wrapped up and saved for later. +This wrapped-up code propagates through the program, until at the end of compilation, +you have a statically-typed AST of all the stuff the compiler saved for runtime, +which can then be lowered to machine code. + +Ultimately, this means you get all the benefits of a high-level language, without any +cost at runtime! And while the compiler currently doesn't emit terribly fast code +(since it was written from scratch in the last week of the langjam), there's no +reason it couldn't eventually employ the same optimizations as any other +statically-typed language. + +--- + +## Syntax + +#### Tokens + +| Token | Description | Examples | +|---|---|---| +| `Int` | Any number of decimal digits. | `12` `04` `512` | +| `Coeff` | An `Int` immediately preceding a non-digit character. | `3x` `4(2)` | +| `(` `)` | Left `(` and right `)` parentheses. Delimiter. | `(` `)` | +| `[` `]` | Left `[` and right `]` square brackets. Delimiter. | `[` `]` | +| `|` | A pipe `|` character. Delimiter. | `|` `|` | +| `.` | A single dot `.`. Delimiter. | `.` | +| `String` | Any number of characters between two double-quotes `"`. Delimiter. | `"hello"` `"cat"` `"line\nbreak"` | +| `Symbol` | Any number of non-delimiter, non-space characters not otherwise covered by a token. Cannot start with a digit or underscore `_`. Includes tokens of repeated dots `.` or colons `:`. | `x` `Y2` `*name*` `..` `::` | +| `Plus` | A single `+` immediately preceding an alphanumeric character or delimiter. | `+1` `+y` | +| `Minus` | A single `-` immediately preceding an alphanumeric character or delimiter. | `-3` `-x` | +| `Quote` | A single `:` immediately preceding any non-space character. | `:yes` `:(1 2 3)` | +| `Newline` | A line break character. | `\n` | + +#### EBNF Grammar + +```ebnf +Term := Int (* examples: 12 22 37 *) + | String (* examples: "hello" "world") *) + | Symbol (* examples: abc Fromage12 ke-bab *) + | ( Term+ ) (* examples: (+ 1 2) (5) *) + | [ Term* ] (* examples: [1 2 3] [:yes :no] *) + | | Term* | (* examples: |+ 1 2| |my-var| *) + | Coeff Term (* examples: 3x 45(5) 2(x - 1) *) + | Plus Term (* examples: +4 +56 +x +(1 + 2) *) + | Minus Term (* examples: -4 -56 -x -(1 + 2) *) + | Quote Term (* examples: :hello :(my-list) *) + | Term . Term (* examples: list.tail 1.inc *) + ; + +Line := Term* Newline [ Line* ]¹ [Quote Line]*² + +Program := Line* +``` +1. Indented block. Indentation is context-sensitive, the block continues until the indentation level is less than or equal to the starting line's indentation. Each line is appended as a list to the starting line's contents. +2. Continuation line. The `Quote` token is included in each line. The contents of each line are appended to the starting line. + +Out of the above grammar, the following terminals produce single terms in the source +code: + + * `Int` generates an integer term. + * `String` generates a string term. + * `Symbol` generates a symbol term. + +The following productions produce lists of their contents: + + * `( Term+ )` generates a list of each of its element terms. + * `[ Term* ]` generates the list `(list-of Term0 Term1 ... TermN)`. + * `| Term* |` generates the list `(splice Term0 Term1 ... TermN)`. + * `Coeff Term` generates the list `(* Coeff Term)`. + * `Plus Term` generates the list `(+ 0 Term)`. + * `Minus Term` generates the list `(- 0 Term)`. + * `Quote Term` generates the list `(quote Term)`. + * `Term . Term` generates the list `(Term Term)`. + * `Line` generates a list of each of its element terms. + * `Program` generates a list `(do Line0 Line1 ... LineN)` for each of its lines. + +--- + +## Evaluation + +Evaluation is the process of converting Basil code to Basil values. Every Basil term +can be evaluated, and there are a number of distinct evaluation processes depending on +a given term's contents. Each kind of evaluation is called a 'form', and forms that +are based on special symbols baked into the compiler are called 'special forms'. + +#### Preparation + +Before evaluation occurs, a term must first be prepared. Preparation consists of a few +passes the compiler does to manipulate the term so it can be evaluated properly. This +includes, in order: + + * Expanding `splice` special forms. + * Finding macro definitions. + * Expanding macro invocations. + * Finding variable and procedure definitions. + * Transforming lists based on infix rules. + +The associated special forms for each of these phases are described later in this +section. + +#### Environments + +All evaluation occurs within an environment. Environments are where variables are +defined and stored. Basil is lexically-scoped, which means that environments are based +on the syntax of the language - e.g. all expressions at the top-level of the program +are evaluated in a _global_ environment, while expressions within a top-level function +definition are evaluated in a _local_ function environment that descends from the +global environment. + +#### Primitives + +Integers, strings, and symbols all evaluate by themselves, without any particular +form. + +Integer and string constants evaluate to themselves, e.g. `10` evaluates to `10`. + +Symbols evaluate to a variable value. The symbol name is looked up in the current +environment. If it exists in the current environment or any parent environment, the +evaluation result is whatever value is bound to that variable. If it doesn't exist, +the compiler errors. + +#### Special Forms + +1. Quote + +``` +quote +``` + +The `quote` special form returns its value without evaluating it. For example, a +quoted symbol will be an actual symbol value, not a variable looked up in some +environment. + +2. Splice + +``` +splice ... +``` + +The `splice` special form evaluates a list of the values provided. It takes the +resulting value, and replaces itself with that value in the source code. + +3. Do + +``` +do ... +``` + +The `do` special form evaluates each of its body terms in order, and returns the last +evaluation result. + +4. Definition + +``` +def +def ( [argument0] [argument1] ... [argumentN]) [body1] ... [bodyN] +``` + +The first `def` form defines a variable in the current environment, initialized with +the provided value. + +The second `def` form defines a procedure in the current environment. The procedure's +name is the first element of the argument list. The rest of the values in the +argument list are declared in a new, local environment as argument variables. Each of +the procedure's body terms are visited in the local environment, but aren't evaluated. +The procedure's body terms are wrapped in a `do` special form. + +Arguments in the second form may either be symbols (which define variables as +described), _or_ quoted symbols, which define keywords. Keywords represent custom +syntax for the function call, not additional parameters. For example, for a procedure +`for in `, the `in` term would be a keyword - used to write the +procedure more cleanly, but not representing a value. + +The evaluation result of either of these forms is the empty list. + +5. Macro Definition + +``` +macro +macro ( [argument0] [argument1] ... [argumentN]) [body1] ... [bodyN] +``` + +The first `macro` form defines a macro variable, or alias. When expanded, it replaces +itself with the term it was initialized to. The term in this form is not evaluated. + +The second `macro` form defines a macro procedure. Like a normal procedure, the +procedure name, arguments, and any keywords are defined in an argument list. Also +like a normal procedure, all of the body terms are wrapped in a `do` special form. +Unlike a normal procedure, however, none of the body terms are prepared. Splices, +other macro expansions, infix parsing - all of this waits until after the macro is +expanded. + +The evaluation result of either of these forms is the empty list. + +6. Infix Definitions + +``` +infix [precedence] ( [argument1] ... [argumentN]) [body1] ... [bodyN] +infix-macro [precedence] ( [argument1] ... [argumentN]) [body1] ... [bodyN] +``` + +The `infix` form defines an infix procedure. This behaves identically to `def` with +regards to creating a procedure, with the addition of infix capabilities. The infix +precedence of the procedure can optionally be specified, with an integer constant. +Infix procedures must have at least one argument, and the name of the procedure is the +second term in the argument list, not the first. + +The `infix-macro` form is the same as `infix`, but defines a macro procedure instead +of a procedure. + +The evaluation result of either of these forms is the empty list. + +7. If + +``` +if * [elif *]* else * +``` + +The `if` form creates an if expression. If the provided condition is +true, the provided if-body is evaluated and returned. Otherwise, the provided +else-body is evaluated and returned. If one or more `elif` keywords are provided +before the else, their respective conditions are evaluated, and if true their +respective bodies are evaluated and returned. + +All bodies (if-body, elif-body, and else-body) above can contain any number of +terms. These terms are wrapped in `do` forms implicitly. + +8. While + +``` +while [body1] ... [bodyN] +``` + +The `while` form creates a while statement. As long as the provided condition is true, +the body terms are repeatedly evaluated. When the loop terminates, the resulting +value is the empty list. Like `if` and procedure bodies, the body terms in a `while` +are implicitly wrapped in a `do` form. + +9. List Of + +``` +list-of [value0] [value1] ... [valueN] +``` + +The `list-of` form returns a list value containing each of the provided values as +elements. + +10. Lambda + +``` +lambda ([argument0] [argument1] ... [argumentN]) [body1] ... [bodyN] +``` + +The `lambda` form creates an anonymous function. It behaves identically to a +procedure definition, with the exception that the argument list does not contain a +name. Unlike a procedure definition, which evaluates to the empty list, a `lambda` +evaluates to a function value - the function it declares. + +#### General Forms + +1. Procedure Invocation + +``` + [argument0] [argument1] ... [argumentN] +``` + +This form invokes a procedure on a number of arguments. The arguments are each +evaluated, passed into the procedure, and the evaluation result is whatever the +procedure returns. The compiler errors if the number of arguments provided is +incorrect for the function. + +In each position where the procedure has a keyword argument, the keyword may be +provided either quoted (`:`) or unquoted (just ``). If the +correct keyword is not provided in either form, the compiler errors. + +2. Macro Expansion + +``` + [argument0] [argument1] ... [argumentN] +``` + +This form is identical to the procedure invocation form, with the caveat that instead +of invoking a function, it is expanding a macro. The arguments are not evaluated. +When they are passed to the macro, instead of a value being created, any splice +forms in the macro body are expanded, then the resulting body replaces the macro +expansion in the source code. + +3. Singleton Evaluation + +``` + +``` + +If a list contains a single term that is not a procedure or macro procedure, then the +list evaluates to whatever that term evaluates to. Essentially, wrapping things in +parentheses more times doesn't change the value. + +4. Infix Parsing + +``` + [argument1] ... [argumentN] +``` + +If no other form applies, the list will attemptedly handle any infix procedures +within itself, rearranging terms until they can be evaluated with other forms. + +All operators are left-associative. The higher an operator's precedence, the more +tightly it binds to arguments. For example, `1 + 2 * 3` translates to `(+ 1 (* 2 3))`, +because `*` has higher precedence than `+`. If only one operator contests a value, +such as if a unary operator is between a value and a binary operator, or for the +interior values of a ternary-or-higher operator, precedence is ignored and the +operator is applied to the value. + +The result of infix parsing is a list of other general forms - invocations, +expansions, and singletons - equivalent to the provided infix expressions. The +original list is replaced in the source code with this processed list. + +--- + +## Types + +Basil has only a small number of built-in types. + +#### Primitives + +The `Int` type represents a negative or positive integer, with a bit width equal to +the word size of the machine. + +The `Symbol` type represents a unique quoted symbol, with a bit width equal to +the word size of the machine. + +The `String` type describes a text string, represented by a pointer to a +null-terminated array of bytes. A string's data is not mutable. + +The `Bool` type describes a true or false value, represented by a word-size integer. +Zero represents false, any other value represents true. + +#### Lists + +The `List` type is parameterized by an element type. For example, an `Int List` is a +list where each element has type `Int`. + +List instances are cons cells - pointers to pairs, the first element being a list element, the second element being a pointer to the next cell in the list. A list's +data is not mutable. + +The empty list has a special type `Void`. Its runtime representation is the null +pointer. + +#### Functions + +Function types are parameterized by two types: the argument type and return type. For +example, the type `Int -> Int` describes a function that accepts one integer argument +and returns an integer. + +Functions are not currently implemented at runtime. They will eventually be +implemented as pointers to closures or raw function pointers (if no captures). + +#### Unification + +Unification is a relation from two source types to a destination type. The +destination type must be a type both source types are compatible with. + +The `Void` type can unify with any `List` type, for example. + +#### Type Variables + +Untyped variables in Basil are assigned unique type variables. An unbound type variable can unify with any other type, binding to that type in the process. A bound +type variable acts identically to the type it is bound to. + +Type variables can parameterize compound types. When this is the case, said +compound types can unify with other compound types of the same kind, unifying the +type variable parameter with the corresponding parameter in the other type. For +example, a `'T0 List` can unify with an `Int List`, binding `'T0` to `Int` in the +process. + +--- + +## Built-In Procedures + +This is a table of all the built-in procedures and variables in Basil. They are +specified in terms of the types they accept and produce. + +| Name | Type | Description | +|---|---|---| +| `true` | `Bool` | Boolean true value. | +| `false` | `Bool` | Boolean false value. | +| `+` | `Int * Int -> Int` | Adds two integers. | +| `-` | `Int * Int -> Int` | Subtracts an integer from another. | +| `*` | `Int * Int -> Int` | Multiplies two integers. | +| `/` | `Int * Int -> Int` | Divides an integer by another. | +| `%` | `Int * Int -> Int` | Finds the remainder of dividing an integer by another. | +| `and` | `Bool * Bool -> Bool` | Logical AND operation on two booleans. | +| `or` | `Bool * Bool -> Bool` | Logical OR operation on two booleans. | +| `xor` | `Bool * Bool -> Bool` | Logical XOR operation on two booleans. | +| `not` | `Bool -> Bool` | Logical NOT operation on a boolean. | +| `==`¹ | `'T0 * 'T0 -> Bool` | Returns whether two values are equal. | +| `!=`¹ | `'T0 * 'T0 -> Bool` | Returns whether two values are not equal. | +| `<`² | `'T0 * 'T0 -> Bool` | Returns if the first value is less than the second. | +| `<=`² | `'T0 * 'T0 -> Bool` | Returns if the first value is less than or equal to the second. | +| `>`² | `'T0 * 'T0 -> Bool` | Returns if the first value is greater than the second. | +| `>=`² | `'T0 * 'T0 -> Bool` | Returns if the first value is greater than or equal to the second. | +| `head` | `'T0 List -> 'T0` | Returns the first value in a list. | +| `tail` | `'T0 List -> 'T0 List` | Returns the rest of a list. | +| `::` | `'T0 * 'T0 List -> 'T0 List` | Creates a list of a value and existing list. | +| `empty?` | `'T0 List -> Bool` | Returns whether a provided list is empty. | +| `?`³ | `Bool * 'T0 * 'T0 -> 'T0` | Ternary conditional operator. | +| `=` | `Symbol * 'T0 -> Void` | Assigns variable to value. | +| `display` | `'T0 -> Void` | Prints a value to standard output on its own line. | + +1. Value equality for integers, bools, strings, and symbols; reference equality for +lists. + +2. Can only compare integers and strings. Integers are ordered numerically, strings +lexicographically by UTF-8 code point. + +3. Only evaluates one path. Behaves similarly to the `if` special form with no +`elif`s. + +--- + +## Compilation + +Sometimes there are values or operations that we might not want the compiler +to perform on its own. Stateful operations, such as printing a line to output, are +things we'd expect to happen when our program is executed, not when it's compiled. +Non-terminating code can be intended functionality, but we'd like our compiler to +terminate so it can produce an executable. + +For all such values, we define a new concept called a runtime value. Runtime +values are Basil values that wrap impure operations, allowing us to operate over +them without actually performing something impure. + +#### Runtime Values + +We define a parametric type `Runtime`. `'T0 Runtime` for some `'T0` describes a typed +expression tree that yields `'T0`. The representation of this type is a node in a +typed AST. + +#### Lowering + +Many compile-time values can be converted to runtime values. This process is called +lowering, and is defined as follows. + + * Integers are lowered to integer constants. + * Booleans are lowered to boolean constants. + * Strings are lowered to string constants. + * Symbols are lowered to symbol constants. + * Lists are lowered to trees of `cons` operations. Each list element is lowered, + and `cons`'d into the list as necessary to produce a list at runtime with the same + elements and structure. + * Empty lists are lowered to void constants. + +It is a compiler error to lower any other kind of value. + +#### Runtime Propagation + +Runtime values originate from stateful code. This includes: + + * Mutation (the assignment `=` operator) of a variable. + * `while` statements. + * IO operations, such as `display`. + * Any code not guaranteed to terminate, such as recursive functions. + +The evaluation result of any of the above operations is a runtime value. Mutating a +variable additionally lowers the variable being assigned to (if it wasn't runtime +already). + +Variables can be bound to runtime values, and when evaluated will evaluate to +runtime values. The resulting runtime value might not be identical (it might just +load the variable from a runtime location), but it will have the same runtime type +as the variable's bound value. + +A procedure invocation on one or more runtime values must return a runtime value, +and lower all non-runtime values it is given. The procedure itself must also be +lowered. + +An if expression or ternary built-in with a runtime condition must evaluate and lower +all bodies that descend from it. An if expression or ternary with a non-runtime +condition and a runtime body only needs to return a runtime value if that runtime +body is reached. + +#### Compilation + +After the entire program is evaluated, we are left with a single value. If this value +is not a runtime value, then the entire program must have been evaluable at +compile-time. In this case, we lower the value before code generation. Otherwise, +the value is a runtime value containing an expression tree of all program code +left to be resolved at runtime. In this case, we can enter code generation without +any additional lowering. + +The code generation and optimization process is not specified here. But the output of +this phase must be executable machine code for some target architecture or virtual +machine. This machine code can either be written to an executable, or executed +directly by the Basil compiler environment. \ No newline at end of file diff --git a/ast.cpp b/ast.cpp new file mode 100644 index 0000000..01fc509 --- /dev/null +++ b/ast.cpp @@ -0,0 +1,796 @@ +#include "ast.h" +#include "values.h" +#include "env.h" +#include "type.h" + +namespace basil { + ASTNode::ASTNode(SourceLocation loc): + _loc(loc), _type(nullptr) {} + + ASTNode::~ASTNode() {} + + SourceLocation ASTNode::loc() const { + return _loc; + } + + const Type* ASTNode::type() { + if (!_type) _type = lazy_type(); + if (_type->kind() == KIND_TYPEVAR + && ((const TypeVariable*)_type)->actual() != ANY) + return ((const TypeVariable*)_type)->actual(); // unwrap concrete typevars + return _type; + } + + ASTSingleton::ASTSingleton(const Type* type): + ASTNode(NO_LOCATION), _type(type) {} + + const Type* ASTSingleton::lazy_type() { + return _type; + } + + Location ASTSingleton::emit(Function& func) { + return ssa_none(); + } + + void ASTSingleton::format(stream& io) const { + write(io, ""); + } + + ASTVoid::ASTVoid(SourceLocation loc): + ASTNode(loc) {} + + const Type* ASTVoid::lazy_type() { + return VOID; + } + + Location ASTVoid::emit(Function& function) { + return ssa_immediate(0); + } + + void ASTVoid::format(stream& io) const { + write(io, "[]"); + } + + ASTInt::ASTInt(SourceLocation loc, i64 value): + ASTNode(loc), _value(value) {} + + const Type* ASTInt::lazy_type() { + return INT; + } + + Location ASTInt::emit(Function& func) { + return ssa_immediate(_value); + } + + void ASTInt::format(stream& io) const { + write(io, _value); + } + + ASTSymbol::ASTSymbol(SourceLocation loc, u64 value): + ASTNode(loc), _value(value) {} + + const Type* ASTSymbol::lazy_type() { + return SYMBOL; + } + + Location ASTSymbol::emit(Function& func) { + return ssa_immediate(_value); + } + + void ASTSymbol::format(stream& io) const { + write(io, symbol_for(_value)); + } + + ASTString::ASTString(SourceLocation loc, const string& value): + ASTNode(loc), _value(value) {} + + const Type* ASTString::lazy_type() { + return STRING; + } + + Location ASTString::emit(Function& func) { + return func.add(new AddressInsn(ssa_const(ssa_next_label(), _value), type())); + } + + void ASTString::format(stream& io) const { + write(io, _value); + } + + ASTBool::ASTBool(SourceLocation loc, bool value): + ASTNode(loc), _value(value) {} + + const Type* ASTBool::lazy_type() { + return BOOL; + } + + Location ASTBool::emit(Function& func) { + return ssa_immediate(_value ? 1 : 0); + } + + void ASTBool::format(stream& io) const { + write(io, _value); + } + + ASTVar::ASTVar(SourceLocation loc, const ref env, u64 name): + ASTNode(loc), _env(env), _name(name) {} + + const Type* ASTVar::lazy_type() { + const Def* def = _env->find(symbol_for(_name)); + if (def && def->value.is_runtime()) + return ((const RuntimeType*)def->value.type())->base(); + + err(loc(), "Undefined variable '", symbol_for(_name), "'."); + return ERROR; + } + + Location ASTVar::emit(Function& func) { + const Def* def = _env->find(symbol_for(_name)); + if (!def) return ssa_none(); + return func.add(new LoadInsn(def->location)); + } + + void ASTVar::format(stream& io) const { + write(io, symbol_for(_name)); + } + + ASTUnary::ASTUnary(SourceLocation loc, ASTNode* child): + ASTNode(loc), _child(child) { + _child->inc(); + } + + ASTUnary::~ASTUnary() { + _child->dec(); + } + + ASTBinary::ASTBinary(SourceLocation loc, ASTNode* left, ASTNode* right): ASTNode(loc), _left(left), _right(right) { + _left->inc(), _right->inc(); + } + + ASTBinary::~ASTBinary() { + _left->dec(), _right->dec(); + } + + ASTBinaryMath::ASTBinaryMath(SourceLocation loc, ASTMathOp op, + ASTNode* left, ASTNode* right): + ASTBinary(loc, left, right), _op(op) {} + + const Type* ASTBinaryMath::lazy_type() { + if (_left->type() == ERROR || _right->type() == ERROR) return ERROR; + const Type* lt = unify(_left->type(), INT); + const Type* rt = unify(_right->type(), INT); + const Type* result = unify(lt, rt); + + if (result != INT) { + err(loc(), "Invalid parameters to arithmetic expression: '", + _left->type(), "' and '", _right->type(), "'."); + return ERROR; + } + return result; + } + + Location ASTBinaryMath::emit(Function& func) { + switch (_op) { + case AST_ADD: + return func.add(new AddInsn(_left->emit(func), _right->emit(func))); + case AST_SUB: + return func.add(new SubInsn(_left->emit(func), _right->emit(func))); + case AST_MUL: + return func.add(new MulInsn(_left->emit(func), _right->emit(func))); + case AST_DIV: + return func.add(new DivInsn(_left->emit(func), _right->emit(func))); + case AST_REM: + return func.add(new RemInsn(_left->emit(func), _right->emit(func))); + default: + return ssa_none(); + } + } + + static const char* MATH_OP_NAMES[] = { + "+", "-", "*", "/", "%" + }; + + void ASTBinaryMath::format(stream& io) const { + write(io, "(", MATH_OP_NAMES[_op], " ", _left, " ", _right, ")"); + } + + ASTBinaryLogic::ASTBinaryLogic(SourceLocation loc, ASTLogicOp op, + ASTNode* left, ASTNode* right): + ASTBinary(loc, left, right), _op(op) {} + + const Type* ASTBinaryLogic::lazy_type() { + if (_left->type() == ERROR || _right->type() == ERROR) return ERROR; + const Type* lt = unify(_left->type(), BOOL); + const Type* rt = unify(_right->type(), BOOL); + const Type* result = unify(lt, rt); + + if (result != BOOL) { + err(loc(), "Invalid parameters to logical expression: '", + _left->type(), "' and '", _right->type(), "'."); + return ERROR; + } + return BOOL; + } + + Location ASTBinaryLogic::emit(Function& func) { + switch (_op) { + case AST_AND: + return func.add(new AndInsn(_left->emit(func), _right->emit(func))); + case AST_OR: + return func.add(new OrInsn(_left->emit(func), _right->emit(func))); + case AST_XOR: + return func.add(new XorInsn(_left->emit(func), _right->emit(func))); + default: + return ssa_none(); + } + } + + static const char* LOGIC_OP_NAMES[] = { + "and", "or", "xor", "not" + }; + + void ASTBinaryLogic::format(stream& io) const { + write(io, "(", LOGIC_OP_NAMES[_op], " ", _left, " ", _right, ")"); + } + + ASTNot::ASTNot(SourceLocation loc, ASTNode* child): + ASTUnary(loc, child) {} + + const Type* ASTNot::lazy_type() { + if (_child->type() == ERROR) return ERROR; + if (unify(_child->type(), BOOL) != BOOL) { + err(loc(), "Invalid argument to 'not' expression: '", + _child->type(), "'."); + return ERROR; + } + return BOOL; + } + + Location ASTNot::emit(Function& function) { + return function.add(new NotInsn(_child->emit(function))); + } + + void ASTNot::format(stream& io) const { + write(io, "(not ", _child, ")"); + } + + ASTBinaryEqual::ASTBinaryEqual(SourceLocation loc, ASTEqualOp op, + ASTNode* left, ASTNode* right): + ASTBinary(loc, left, right), _op(op) {} + + const Type* ASTBinaryEqual::lazy_type() { + if (_left->type() == ERROR || _right->type() == ERROR) return ERROR; + return BOOL; + } + + Location ASTBinaryEqual::emit(Function& func) { + if (_left->type() == STRING || _right->type() == STRING) { + func.add(new StoreArgumentInsn(_left->emit(func), 0, _left->type())); + func.add(new StoreArgumentInsn(_right->emit(func), 1, _right->type())); + Location result = func.add(new CallInsn(ssa_find_label("_strcmp"), INT)); + return func.add(new EqualInsn(result, ssa_immediate(0))); + } + + switch (_op) { + case AST_EQUAL: + return func.add(new EqualInsn(_left->emit(func), _right->emit(func))); + case AST_INEQUAL: + return func.add(new InequalInsn(_left->emit(func), _right->emit(func))); + default: + return ssa_none(); + } + } + + static const char* EQUAL_OP_NAMES[] = { + "==", "!=" + }; + + void ASTBinaryEqual::format(stream& io) const { + write(io, "(", EQUAL_OP_NAMES[_op], " ", _left, " ", _right, ")"); + } + + ASTBinaryRel::ASTBinaryRel(SourceLocation loc, ASTRelOp op, + ASTNode* left, ASTNode* right): + ASTBinary(loc, left, right), _op(op) {} + + const Type* ASTBinaryRel::lazy_type() { + if (_left->type() == ERROR || _right->type() == ERROR) return ERROR; + const Type* lt = unify(_left->type(), INT); + const Type* rt = unify(_right->type(), INT); + const Type* result = unify(lt, rt); + + if (result != INT) { + lt = unify(_left->type(), STRING); + rt = unify(_right->type(), STRING); + result = unify(lt, rt); + if (result != STRING) { + err(loc(), "Invalid parameters to relational expression: '", + _left->type(), "' and '", _right->type(), "'."); + return ERROR; + } + } + return BOOL; + } + + Location ASTBinaryRel::emit(Function& func) { + if (_left->type() == STRING || _right->type() == STRING) { + func.add(new StoreArgumentInsn(_left->emit(func), 0, _left->type())); + func.add(new StoreArgumentInsn(_right->emit(func), 1, _right->type())); + Location result = func.add(new CallInsn(ssa_find_label("_strcmp"), INT)); + + switch(_op) { + case AST_LESS: + return func.add(new LessInsn(result, ssa_immediate(0))); + case AST_LESS_EQUAL: + return func.add(new LessEqualInsn(result, ssa_immediate(0))); + case AST_GREATER: + return func.add(new GreaterInsn(result, ssa_immediate(0))); + case AST_GREATER_EQUAL: + return func.add(new GreaterEqualInsn(result, ssa_immediate(0))); + default: + return ssa_none(); + } + } + + switch (_op) { + case AST_LESS: + return func.add(new LessInsn(_left->emit(func), _right->emit(func))); + case AST_LESS_EQUAL: + return func.add(new LessEqualInsn(_left->emit(func), _right->emit(func))); + case AST_GREATER: + return func.add(new GreaterInsn(_left->emit(func), _right->emit(func))); + case AST_GREATER_EQUAL: + return func.add(new GreaterEqualInsn(_left->emit(func), _right->emit(func))); + default: + return ssa_none(); + } + } + + static const char* REL_OP_NAMES[] = { + "<", "<=", ">", ">=" + }; + + void ASTBinaryRel::format(stream& io) const { + write(io, "(", REL_OP_NAMES[_op], " ", _left, " ", _right, ")"); + } + + ASTDefine::ASTDefine(SourceLocation loc, ref env, u64 name, ASTNode* value): + ASTUnary(loc, value), _env(env), _name(name) {} + + const Type* ASTDefine::lazy_type() { + return VOID; + } + + Location ASTDefine::emit(Function& func) { + Location loc = func.create_local(symbol_for(_name), type()); + _env->find(symbol_for(_name))->location = loc; + func.add(new StoreInsn(loc, _child->emit(func), true)); + return ssa_none(); + } + + void ASTDefine::format(stream& io) const { + write(io, "(def ", symbol_for(_name), " ", _child, ")"); + } + + ASTCall::ASTCall(SourceLocation loc, ASTNode* func, const vector& args): + ASTNode(loc), _func(func), _args(args) { + _func->inc(); + for (ASTNode* n : _args) n->inc(); + } + + ASTCall::~ASTCall() { + _func->dec(); + for (ASTNode* n : _args) n->dec(); + } + + const Type* ASTCall::lazy_type() { + const Type* fntype = _func->type(); + const Type* argt = ((const FunctionType*)fntype)->arg(); + if (fntype == ERROR || argt == ERROR) return ERROR; + for (u32 i = 0; i < _args.size(); i ++) { + if (_args[i]->type() == ERROR) return ERROR; + if (!unify(_args[i]->type(), ((const ProductType*)argt)->member(i))) { + err(_args[i]->loc(), "Invalid argument ", i, " to function call."); + return ERROR; + } + } + return ((const FunctionType*)fntype)->ret(); + } + + Location ASTCall::emit(Function& func) { + Location fn = _func->emit(func); + vector arglocs; + const Type* argt = ((const FunctionType*)_func->type())->arg(); + for (u32 i = 0; i < _args.size(); i ++) { + arglocs.push(_args[i]->emit(func)); + } + for (u32 i = 0; i < _args.size(); i ++) { + func.add(new StoreArgumentInsn(arglocs[i], i, + ((const ProductType*)argt)->member(i))); + } + return func.add(new CallInsn(fn.label_index, type())); + } + + void ASTCall::format(stream& io) const { + write(io, "(", _func); + for (ASTNode* n : _args) write(io, " ", n); + write(io, ")"); + } + + const Type* ASTIncompleteFn::lazy_type() { + return find(_args, find()); + } + + ASTIncompleteFn::ASTIncompleteFn(SourceLocation loc, const Type* args, i64 name): + ASTNode(loc), _args(args), _name(name) {} + + Location ASTIncompleteFn::emit(Function& func) { + Location loc; + loc.type = SSA_LABEL; + loc.label_index = ssa_find_label(symbol_for(_name)); + return loc; + } + + void ASTIncompleteFn::format(stream& io) const { + if (_name == -1) write(io, ""); + else write(io, symbol_for(_name)); + } + + ASTFunction::ASTFunction(SourceLocation loc, ref env, const Type* args_type, + const vector& args, ASTNode* body, i64 name): + ASTNode(loc), _env(env), _args_type(args_type), _args(args), + _body(body), _name(name), _emitted(false) { + _body->inc(); + } + + ASTFunction::~ASTFunction() { + _body->dec(); + } + + const Type* ASTFunction::lazy_type() { + if (_args_type == ERROR || _body->type() == ERROR) return ERROR; + return find(_args_type, _body->type()); + } + + Location ASTFunction::emit(Function& func) { + if (!_emitted) { + Function& fn = _name == -1 ? func.create_function() + : func.create_function(symbol_for(_name)); + _label = fn.label(); + for (u32 i = 0; i < _args.size(); i ++) { + Def* def = _env->find(symbol_for(_args[i])); + if (def) def->location = fn.add(new LoadArgumentInsn(i, + ((ProductType*)_args_type)->member(i))); + } + fn.add(new RetInsn(_body->emit(fn))); + + _emitted = true; + } + + Location loc; + loc.type = SSA_LABEL; + loc.label_index = _label; + return loc; + } + + void ASTFunction::format(stream& io) const { + if (_name == -1) write(io, ""); + else write(io, symbol_for(_name)); + } + + ASTBlock::ASTBlock(SourceLocation loc, const vector& exprs): + ASTNode(loc), _exprs(exprs) { + for (ASTNode* n : _exprs) n->inc(); + } + + ASTBlock::~ASTBlock() { + for (ASTNode* n : _exprs) n->dec(); + } + + const Type* ASTBlock::lazy_type() { + for (ASTNode* n : _exprs) if (n->type() == ERROR) return ERROR; + return _exprs.back()->type(); + } + + Location ASTBlock::emit(Function& func) { + Location loc; + for (ASTNode* n : _exprs) loc = n->emit(func); + return loc; + } + + void ASTBlock::format(stream& io) const { + write(io, "(do"); + for (ASTNode* n : _exprs) write(io, " ", n); + write(io, ")"); + } + + ASTIf::ASTIf(SourceLocation loc, ASTNode* cond, ASTNode* if_true, ASTNode* if_false): + ASTNode(loc), _cond(cond), _if_true(if_true), _if_false(if_false) { + _cond->inc(); + _if_true->inc(); + _if_false->inc(); + } + + ASTIf::~ASTIf() { + _cond->dec(); + _if_true->dec(); + _if_false->dec(); + } + + const Type* ASTIf::lazy_type() { + if (_cond->type() == ERROR || _if_true->type() == ERROR + || _if_false->type() == ERROR) return ERROR; + if (unify(_cond->type(), BOOL) != BOOL) { + err(_cond->loc(), "Expected condition of type 'bool', given '", + _cond->type(), "'."); + return ERROR; + } + const Type* left = _if_true->type(), *right = _if_false->type(); + const Type* t = unify(left, right); + if (!t) { + err(loc(), "Could not unify types for branches of if expression: '", + left, "' and '", right, "'."); + return ERROR; + } + + return t; + } + + Location ASTIf::emit(Function& func) { + u32 _else = ssa_next_label(), _end = ssa_next_label(); + Location result = func.create_local(type()); + func.add(new IfZeroInsn(_else, _cond->emit(func))); + Location true_result = _if_true->emit(func); + func.add(new StoreInsn(result, true_result, true)); + func.add(new GotoInsn(_end)); + func.add(new Label(_else)); + Location false_result = _if_false->emit(func); + func.add(new StoreInsn(result, false_result, true)); + func.add(new Label(_end)); + return result; + } + + void ASTIf::format(stream& io) const { + write(io, "(if ", _cond, " ", _if_true, " ", _if_false, ")"); + } + + ASTWhile::ASTWhile(SourceLocation loc, ASTNode* cond, ASTNode* body): + ASTNode(loc), _cond(cond), _body(body) { + _cond->inc(); + _body->inc(); + } + + ASTWhile::~ASTWhile() { + _cond->dec(); + _body->dec(); + } + + const Type* ASTWhile::lazy_type() { + const Type* t = unify(_cond->type(), BOOL); + if (!t) { + err(loc(), "Invalid condition in 'while' statement: '", + _cond->type(), "'."); + return ERROR; + } + if (_body->type() == ERROR) return ERROR; + return VOID; + } + + Location ASTWhile::emit(Function& func) { + u32 _start = ssa_next_label(), _end = ssa_next_label(); + Location result = func.create_local(type()); + func.add(new Label(_start)); + func.add(new IfZeroInsn(_end, _cond->emit(func))); + _body->emit(func); + func.add(new GotoInsn(_start)); + func.add(new Label(_end)); + return result; + } + + void ASTWhile::format(stream& io) const { + write(io, "(while ", _cond, " ", _body, ")"); + } + + ASTIsEmpty::ASTIsEmpty(SourceLocation loc, ASTNode* list): + ASTUnary(loc, list) {} + + const Type* ASTIsEmpty::lazy_type() { + if (_child->type() == ERROR) return ERROR; + const Type* ct = _child->type(); + if (ct->kind() != KIND_LIST && !ct->concrete()) + ct = unify(ct, find(find())); + if (!ct || (ct->kind() != KIND_LIST && ct != VOID)) { + err(_child->loc(), "Invalid argument to 'empty?' expression: '", + _child->type(), "'."); + return ERROR; + } + return BOOL; + } + + Location ASTIsEmpty::emit(Function& function) { + return function.add(new EqualInsn(_child->emit(function), ssa_immediate(0))); + } + + void ASTIsEmpty::format(stream& io) const { + write(io, "(empty? ", _child, ")"); + } + + ASTHead::ASTHead(SourceLocation loc, ASTNode* list): + ASTUnary(loc, list) {} + + const Type* ASTHead::lazy_type() { + if (_child->type() == ERROR) return ERROR; + const Type* ct = _child->type(); + if ((ct->kind() != KIND_LIST && !ct->concrete()) || ct == VOID) + ct = unify(ct, find(find())); + if (!ct || ct->kind() != KIND_LIST) { + err(_child->loc(), "Invalid argument to 'head' expression: '", + _child->type(), "'."); + return ERROR; + } + return ((const ListType*) _child->type())->element(); + } + + Location ASTHead::emit(Function& function) { + return function.add(new LoadPtrInsn(_child->emit(function), type(), 0)); + } + + void ASTHead::format(stream& io) const { + write(io, "(head ", _child, ")"); + } + + ASTTail::ASTTail(SourceLocation loc, ASTNode* list): + ASTUnary(loc, list) {} + + const Type* ASTTail::lazy_type() { + if (_child->type() == ERROR) return ERROR; + const Type* ct = _child->type(); + if ((ct->kind() != KIND_LIST && !ct->concrete()) || ct == VOID) + ct = unify(ct, find(find())); + if (!ct || ct->kind() != KIND_LIST) { + err(_child->loc(), "Invalid argument to 'tail' expression: '", + _child->type(), "'."); + return ERROR; + } + return _child->type(); + } + + Location ASTTail::emit(Function& function) { + return function.add(new LoadPtrInsn(_child->emit(function), type(), 8)); + } + + void ASTTail::format(stream& io) const { + write(io, "(tail ", _child, ")"); + } + + ASTCons::ASTCons(SourceLocation loc, ASTNode* first, ASTNode* rest): + ASTBinary(loc, first, rest) {} + + const Type* ASTCons::lazy_type() { + const Type *first = _left->type(), *rest = _right->type(); + if (first == ERROR || rest == ERROR) return ERROR; + if (rest == VOID) return find(first); + else { + if (rest->kind() == KIND_TYPEVAR) + return unify(rest, find(first)); + if (rest->kind() != KIND_LIST) { + err(_right->loc(), "Invalid argument to 'cons' expression."); + return ERROR; + } + const Type* element = ((const ListType*)rest)->element(); + if (unify(first, element) != element) { + err(_left->loc(), "Invalid arguments to 'cons' expression: '", + first, "' and '", rest, "'."); + return ERROR; + } + return rest; + } + } + + Location ASTCons::emit(Function& function) { + Location l = _left->emit(function), r = _right->emit(function); + function.add(new StoreArgumentInsn(l, 0, _left->type())); + function.add(new StoreArgumentInsn(r, 1, _right->type())); + return function.add(new CallInsn(ssa_find_label("_cons"), type())); + } + + void ASTCons::format(stream& io) const { + write(io, "(cons ", _left, " ", _right, ")"); + } + + const Type* ASTDisplay::lazy_type() { + return VOID; + } + + ASTDisplay::ASTDisplay(SourceLocation loc, ASTNode* node): + ASTUnary(loc, node) {} + + Location ASTDisplay::emit(Function& function) { + const char* name; + if (_child->type() == INT) name = "_display_int"; + else if (_child->type() == SYMBOL) name = "_display_symbol"; + else if (_child->type() == BOOL) name = "_display_bool"; + else if (_child->type() == STRING) name = "_display_string"; + else if (_child->type() == find(INT)) name = "_display_int_list"; + else if (_child->type() == find(SYMBOL)) name = "_display_symbol_list"; + else if (_child->type() == find(BOOL)) name = "_display_bool_list"; + else if (_child->type() == find(STRING)) name = "_display_string_list"; + else if (_child->type() == VOID) name = "_display_int_list"; + function.add(new StoreArgumentInsn(_child->emit(function), 0, _child->type())); + return function.add(new CallInsn(ssa_find_label(name), type())); + } + + void ASTDisplay::format(stream& io) const { + write(io, "(display ", _child, ")"); + } + + const Type* ASTReadLine::lazy_type() { + return STRING; + } + + ASTReadLine::ASTReadLine(SourceLocation loc) : ASTNode(loc) {} + + Location ASTReadLine::emit(Function& function) { + return function.add(new CallInsn(ssa_find_label("_read_line"), STRING)); + } + + void ASTReadLine::format(stream& io) const { + write(io, "(read-line)"); + } + + // const Type* ASTNativeCall::lazy_type() { + + // } + + // ASTNativeCall::ASTNativeCall(SourceLocation loc, const string &func, vector args) + // : ASTNode(loc), _func(func), _args(args) {} + + // Location ASTNativeCall::emit(Function& function) { + // for (int i = 0; i < _args.size(); i ++) + // function.add(new StoreArgumentInsn(_args[i]->emit(function), i, _args[i]->type())); + // // return function.add(new CallInsn(ssa_find_label(_func), )); + // } + + // void ASTNativeCall::format(stream& io) const { + // write(io, "(", _func, _args.size() ? " " : ""); + // for (const ASTNode *arg : _args) { + // write(io, " ", arg); + // } + // write(io, ")"); + // } + + + + ASTAssign::ASTAssign(SourceLocation loc, const ref env, + u64 dest, ASTNode* src): ASTUnary(loc, src), _env(env), _dest(dest) {} + + const Type* ASTAssign::lazy_type() { + const Type* src_type = _child->type(); + const Def* def = _env->find(symbol_for(_dest)); + const Type* dest_type = ((const RuntimeType*)def->value.type())->base(); + if (src_type == ERROR || dest_type == ERROR) + return ERROR; + if (!unify(src_type, dest_type)) { + err(loc(), "Invalid arguments to assignment '", src_type, "' and '", dest_type, "'."); + return ERROR; + } + return VOID; + } + + Location ASTAssign::emit(Function& func) { + const Def* def = _env->find(symbol_for(_dest)); + if (!def) return ssa_none(); + return func.add(new StoreInsn(def->location, _child->emit(func), false)); + } + + void ASTAssign::format(stream& io) const { + write(io, "(= ", symbol_for(_dest), " ", _child, ")"); + } +} + +void write(stream& io, basil::ASTNode* n) { + n->format(io); +} + +void write(stream& io, const basil::ASTNode* n) { + n->format(io); +} \ No newline at end of file diff --git a/ast.h b/ast.h new file mode 100644 index 0000000..698655e --- /dev/null +++ b/ast.h @@ -0,0 +1,392 @@ +#ifndef BASIL_AST_H +#define BASIL_AST_H + +#include "util/defs.h" +#include "util/str.h" +#include "util/rc.h" +#include "util/io.h" +#include "errors.h" +#include "type.h" +#include "values.h" +#include "ssa.h" + +namespace basil { + class Def; + class Env; + + class ASTNode : public RC { + SourceLocation _loc; + const Type* _type; + protected: + virtual const Type* lazy_type() = 0; + public: + ASTNode(SourceLocation loc); + virtual ~ASTNode(); + + SourceLocation loc() const; + const Type* type(); + virtual Location emit(Function& function) = 0; + virtual void format(stream& io) const = 0; + }; + + class ASTSingleton : public ASTNode { + const Type* _type; + protected: + const Type* lazy_type() override; + public: + ASTSingleton(const Type* type); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTVoid : public ASTNode { + protected: + const Type* lazy_type() override; + public: + ASTVoid(SourceLocation loc); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTInt : public ASTNode { + i64 _value; + protected: + const Type* lazy_type() override; + public: + ASTInt(SourceLocation loc, i64 value); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTSymbol : public ASTNode { + u64 _value; + protected: + const Type* lazy_type() override; + public: + ASTSymbol(SourceLocation loc, u64 value); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTString : public ASTNode { + string _value; + protected: + const Type* lazy_type() override; + public: + ASTString(SourceLocation, const string& value); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTBool : public ASTNode { + bool _value; + protected: + const Type* lazy_type() override; + public: + ASTBool(SourceLocation loc, bool value); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTVar : public ASTNode { + ref _env; + u64 _name; + protected: + const Type* lazy_type() override; + public: + ASTVar(SourceLocation loc, const ref env, u64 name); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTUnary : public ASTNode { + protected: + ASTNode *_child; + public: + ASTUnary(SourceLocation loc, ASTNode* child); + ~ASTUnary(); + }; + + class ASTBinary : public ASTNode { + protected: + ASTNode *_left, *_right; + public: + ASTBinary(SourceLocation loc, ASTNode* left, ASTNode* right); + ~ASTBinary(); + }; + + enum ASTMathOp { + AST_ADD, + AST_SUB, + AST_MUL, + AST_DIV, + AST_REM + }; + + class ASTBinaryMath : public ASTBinary { + ASTMathOp _op; + protected: + const Type* lazy_type() override; + public: + ASTBinaryMath(SourceLocation loc, ASTMathOp op, + ASTNode* left, ASTNode* right); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + enum ASTLogicOp { + AST_AND, + AST_OR, + AST_XOR, + AST_NOT + }; + + class ASTBinaryLogic : public ASTBinary { + ASTLogicOp _op; + protected: + const Type* lazy_type() override; + public: + ASTBinaryLogic(SourceLocation loc, ASTLogicOp op, + ASTNode* left, ASTNode* right); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTNot : public ASTUnary { + protected: + const Type* lazy_type() override; + public: + ASTNot(SourceLocation loc, ASTNode* child); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + enum ASTEqualOp { + AST_EQUAL, + AST_INEQUAL + }; + + class ASTBinaryEqual : public ASTBinary { + ASTEqualOp _op; + protected: + const Type* lazy_type() override; + public: + ASTBinaryEqual(SourceLocation loc, ASTEqualOp op, + ASTNode* left, ASTNode* right); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + enum ASTRelOp { + AST_LESS, + AST_LESS_EQUAL, + AST_GREATER, + AST_GREATER_EQUAL + }; + + class ASTBinaryRel : public ASTBinary { + ASTRelOp _op; + protected: + const Type* lazy_type() override; + public: + ASTBinaryRel(SourceLocation loc, ASTRelOp op, + ASTNode* left, ASTNode* right); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTDefine : public ASTUnary { + ref _env; + u64 _name; + protected: + const Type* lazy_type() override; + public: + ASTDefine(SourceLocation loc, ref env, u64 name, ASTNode* value); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTCall : public ASTNode { + ASTNode* _func; + vector _args; + protected: + const Type* lazy_type() override; + public: + ASTCall(SourceLocation loc, ASTNode* func, const vector& args); + ~ASTCall(); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTIncompleteFn : public ASTNode { + const Type* _args; + i64 _name; + protected: + const Type* lazy_type() override; + public: + ASTIncompleteFn(SourceLocation loc, const Type* args, i64 name); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTFunction : public ASTNode { + ref _env; + const Type* _args_type; + vector _args; + ASTNode* _body; + i64 _name; + bool _emitted; + u32 _label; + protected: + const Type* lazy_type() override; + public: + ASTFunction(SourceLocation loc, ref env, const Type* args_type, + const vector& args, ASTNode* body, i64 name = -1); + ~ASTFunction(); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTBlock : public ASTNode { + vector _exprs; + protected: + const Type* lazy_type() override; + public: + ASTBlock(SourceLocation loc, const vector& exprs); + ~ASTBlock(); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTIf : public ASTNode { + ASTNode *_cond, *_if_true, *_if_false; + protected: + const Type* lazy_type() override; + public: + ASTIf(SourceLocation loc, ASTNode* cond, ASTNode* if_true, ASTNode* if_false); + ~ASTIf(); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTWhile : public ASTNode { + ASTNode *_cond, *_body; + protected: + const Type* lazy_type() override; + public: + ASTWhile(SourceLocation loc, ASTNode* cond, ASTNode* body); + ~ASTWhile(); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTIsEmpty : public ASTUnary { + protected: + const Type* lazy_type() override; + public: + ASTIsEmpty(SourceLocation loc, ASTNode* list); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTHead : public ASTUnary { + protected: + const Type* lazy_type() override; + public: + ASTHead(SourceLocation loc, ASTNode* list); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTTail : public ASTUnary { + protected: + const Type* lazy_type() override; + public: + ASTTail(SourceLocation loc, ASTNode* list); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTCons : public ASTBinary { + protected: + const Type* lazy_type() override; + public: + ASTCons(SourceLocation loc, ASTNode* first, ASTNode* rest); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTDisplay : public ASTUnary { + protected: + const Type* lazy_type() override; + public: + ASTDisplay(SourceLocation loc, ASTNode* node); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTReadLine : public ASTNode { + protected: + const Type* lazy_type() override; + public: + ASTReadLine(SourceLocation loc); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + // class ASTNativeCall : public ASTNode { + // const string _func_name; + // const Type * _ret; + // vector _args; + // protected: + // const Type* lazy_type() override; + // public: + // ASTNativeCall(SourceLocation loc, const string &func_name, vector args); + + // Location emit(Function& function) override; + // void format(stream& io) const override; + // }; + + class ASTAssign : public ASTUnary { + ref _env; + u64 _dest; + protected: + const Type* lazy_type() override; + public: + ASTAssign(SourceLocation loc, const ref env, + u64 dest, ASTNode* src); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; +} + +void write(stream& io, basil::ASTNode* t); +void write(stream& io, const basil::ASTNode* t); + +#endif \ No newline at end of file diff --git a/dev-notes/PROJECT b/dev-notes/PROJECT new file mode 100644 index 0000000..9e6c954 --- /dev/null +++ b/dev-notes/PROJECT @@ -0,0 +1,43 @@ +Project Structure +----------------- + +[Included library code] + +All of the following was implemented before the jam. None of Basil's +functionality is contained within these files, they simply provide an +independent implementation of classes in the C++ standard library. + + - defs.h: a few shared typedefs and forward declarations. + + - hash.h/cpp: a standard polymorphic hash function, + hash set, and hash map based on robin hood probing. + + - io.h/cpp: a suite of variadic io functions and a stream abstraction, + which is implemented both by a file wrapper class and an in-memory buffer. + + - slice.h: a slice type, for representing subranges of strings and + containers. + + - str.h/cpp: a resizable byte string type. implements small string + optimization and only allocates for strings greater than 15 + characters. + + - vec.h: a resizable vector type. + +[Compiler/interpreter code] + +Code for implementing Basil's compiler phases. + + - source.h/cpp: an abstraction over a Basil source file. + + - lex.h/cpp: token type and lexer. + + - parse.h/cpp: parser implementation. + + - ast.h/cpp: nodes for Basil's syntax tree. + + - value.h/cpp: a type for representing any Basil value at compile time. + + - eval.h/cpp: evaluates a syntax tree to a Basil value. + + - main.cpp: driver function for the Basil compiler and utilities. \ No newline at end of file diff --git a/dev-notes/TODO b/dev-notes/TODO new file mode 100644 index 0000000..ee50245 --- /dev/null +++ b/dev-notes/TODO @@ -0,0 +1,148 @@ +Langjam To-do List +------------------ + +Day 1. Lexer and Sources + ✓ Comments: ignore characters up to and including newline. + ✓ Delimiter Tokens (parens, brackets, etc) + ✓ Whitespace: throw away whitespace chars other than newline, which + is a token. + ✓ Integers: any number of decimal digits, terminated by a non-digit + character. + ✓ Symbols: any number of symbols, letters, or numbers. + - Must start with a symbol or letter. + - Cannot start with an underscore. + ✓ Prefix operators: +, -. + - Must be preceded by a space. + - Must be succeeded by a term with no space. + ✓ Dot: . + - Multiple consecutive dots (with no space) form a symbol. + - Otherwise, lexed as a unique dot token. + ✓ Colon: : + - When used as a prefix operator (preceded by space, succeeded + by no space) produces a quote token. + - Otherwise, lexed as a symbol. A symbol of only one colon is + a unique colon token (used for either blocks or infix typing). + +Day 3. Environments + ✓ Create environment type: must be able to store information on + definitions in a scope. + - Start just storing the name of each definition, and some basic + parameters such as kind (is it a variable? procedure? macro?) + and additional information (number of arguments?). + - We can expand this later with types, type variables, and values. + ✓ Implement definition lookup, lexically-scoped. + ✓ Create a root environment: stores built-in functions and variables. + ✓ Create a top-level environment: stores global definitions for + a file. + ✓ Visit each macro and procedure body and create local environments. + - Recursively visit definitions within each local environment. + +Day 5. Types and Values + ✓ Scalar Types: integer and symbol. + - Type type: a type for types. + ✓ Void type: singleton type representing the absence of a value. + - Any type: wildcard type that all types can convert to. + ✓ Symbol values: assign integer values to each symbol. + - This lets symbols be represented by simple words at runtime. + ✓ List Types: type for list parameterized by element. + ✓ Sum Types: type for a value of one of several member types. + - Implicitly convertible to from any of its member types. + - Product Types: type for a value with multiple members. + - Used for multiple arguments? + ✓ Function Types: type for procedure. + - Argument type and return type. + ✓ Type kinds: overall categories of type. + - For example, how do we identify if a given type is a function + type? A product type? + ✓ Type deduplication: avoid instantiating types multiple times. + - Implement hashing and equality for types. + - Implement a way of generating types of different kinds. + ✓ Value type: represents any Basil value within the compiler. + - Has a Basil type. + - Stores a value based on the kind of said type. + - Integers and symbols can be represented plainly within the union. + - All compound types are GC'd with reference counting. + ✓ Arithmetic operations: common math ops with dynamic type checking. + - Add, subtract, etc. + ✓ Comparison operations. + - Equality is defined for integers and symbols trivially. Lists + and functions are equal if their addresses are equal. Sums are + equal if their current types are equal and their values are + equal. Product types are equal if all members are equal. + - Relational operators are only defined for integers. + ✓ List operations: get head and tail, create cons cells. + ✓ Function call: take function value and arguments, produce value. + ✓ Print to stream: format any value and print it. + +Day 6. Parser and Error Handling + ✓ Line numbers: extend tokens with line and column information. + ✓ Error reporting: report errors with varargs at line and column. + ✓ Terminals + - Symbols + - Integers + ✓ Simple, parentheses-enclosed lists. + ✓ Lines: lines of terms at the top level are lists. + - Semicolons: group terms within lists into sublists. + ✓ Indentation: continuations and blocks. + - Indenting after a newline: continuation of the previous list. + - Indenting after a colon and newline: treat each indented line + as a list, add them to previous list. + ✓ Unary ops: plus, minus, quote. + - +x should generate (+ 0 x) + - -x should generate (- 0 x) + - :x should generate (quote x) + ✓ Container expressions: lists and sets. + - [1 2 3] should generate (make-list 1 2 3) + - {1 2 3} should generate (make-set 1 2 3) + ✓ Infix ops: dot and colon. + - x . y should generate (x y) + - x : y should generate (set-type x y) + ✓ Splicing: pipe. + - |+ 1 2| should generate (splice (+ 1 2)) + +Day 8. Evaluation - Special Forms and Definitions + ✓ Quote: return data representation of quoted code. + ✓ Variable definitions: bind values to names in the local + environment. + ✓ Integer constants. + ✓ Arithmetic ops. + ✓ Comparison ops. + ✓ List ops. + ✓ Procedure definitions: create and bind function values to names in + the local environment. + - def (args...) ... + - Will evaluate body terms from first to last when given + arguments. + ✓ Function calls. + +Day 10. Anonymous Functions, Splices, and Macros + ✓ Anonymous functions (lambda). + ✓ Splice: turn value into code, add it to enclosing list. + - Will we need some kind of parent pointer for this? + ✓ Macro definitions: same as ordinary variables and procedures, but + should be visited and handled first. + ✓ Implement macro expansion. + - Macro procedures: parametric, generate code based on arguments. + - Macro variables: aliases for bits of code. + +Day 11. Binary Infix Ops + ✓ Infix definitions: same as procedure definitions. + - Arity must be at least 1. + - Can have optionally-specified precedence. + ✓ Infix parsing: generate equivalent s-expressions for a list. + - Based on precedence. + - Operators are handled by definition, not by value. In other words, + the actual function value for an infix operator doesn't store its + precedence. + ✓ Infix handling in lists. + - Applies when no other form of evaluation does. + ✓ Infix handling in argument lists for procedures. + ✓ Infix handling in argument lists for special forms. + ✓ Coefficients: a number in front of a term multiplies it. + +Day 12. Arbitrary Arity Infix Ops + ✓ Unary operators: called postfix. + ✓ Ternary and higher: called infix. + - Only the first and last terms are affected by precedence. + ✓ Infix macros. + diff --git a/driver.cpp b/driver.cpp new file mode 100644 index 0000000..1cf0244 --- /dev/null +++ b/driver.cpp @@ -0,0 +1,218 @@ +#include "driver.h" +#include "lex.h" +#include "parse.h" +#include "errors.h" +#include "values.h" +#include "native.h" +#include "eval.h" +#include "ssa.h" +#include "ast.h" +#include "util/io.h" + +namespace basil { + static bool _print_tokens = false, + _print_parsed = false, + _print_ast = false, + _print_ssa = false, + _print_asm = false; + + void print_tokens(bool should) { + _print_tokens = should; + } + + void print_parsed(bool should) { + _print_parsed = should; + } + + void print_ast(bool should) { + _print_ast = should; + } + + void print_ssa(bool should) { + _print_ssa = should; + } + + void print_asm(bool should) { + _print_asm = should; + } + + vector lex(Source::View& view) { + vector tokens; + while (view.peek()) tokens.push(scan(view)); + + if (_print_tokens) { + print(BOLDYELLOW); + for (const Token& tok : tokens) print(tok, " "); + println(RESET, "\n"); + } + return tokens; + } + + Value parse(TokenView& view) { + vector results; + while (view.peek()) { + Value line = parse_line(view, view.peek().column); + if (!line.is_void()) results.push(line); + } + + if (_print_parsed) { + print(BOLDGREEN); + for (const Value& v : results) println(v); + println(RESET); + } + return cons(string("do"), list_of(results)); + } + + ref create_global_env() { + ref root = create_root_env(); + Env global_env(root); + return global_env; + } + + void compile(Value value, Object& object, Function& fn) { + fn.allocate(); + fn.emit(object); + ssa_emit_constants(object); + if (error_count()) return; + + if (_print_asm) { + print(BOLDRED); + byte_buffer code = object.code(); + while (code.size()) printf("%02x ", code.read()); + println(RESET, "\n"); + } + + add_native_functions(object); + + object.load(); + } + + void generate(Value value, Function& fn) { + Location last = ssa_none(); + if (value.is_runtime()) last = value.get_runtime()->emit(fn); + if (last.type != SSA_NONE) fn.add(new RetInsn(last)); + if (error_count()) return; + + if (_print_ssa) { + print(BOLDMAGENTA); + print(fn); + println(RESET, "\n"); + } + } + + void compile(Value value, Object& object) { + Function main_fn("main"); + Location last = ssa_none(); + if (value.is_runtime()) last = value.get_runtime()->emit(main_fn); + if (last.type != SSA_NONE) main_fn.add(new RetInsn(last)); + if (error_count()) return; + + if (_print_ssa) { + print(BOLDMAGENTA); + print(main_fn); + println(RESET, "\n"); + } + + compile(value, object, main_fn); + } + + void jit_print(Value value, const Object& object) { + auto main_jit = object.find(jasmine::global("main")); + if (main_jit) { + u64 result = ((u64(*)())main_jit)(); + const Type* t = value.get_runtime()->type(); + if (t == VOID) return; + print("= "); + + if (t == INT) print((i64)result); + else if (t == SYMBOL) print(symbol_for(result)); + else if (t == BOOL) print((bool)result); + else if (t == STRING) print('"', (const char*)result, '"'); + else if (t->kind() == KIND_LIST) display_native_list(t, (void*)result); + println(""); + } + } + + int execute(Value value, const Object& object) { + auto main_jit = object.find(jasmine::global("main")); + if (main_jit) return ((i64(*)())main_jit)(); + return 1; + } + + Value repl(ref global, Source& src, Function& mainfn) { + print("? "); + auto view = src.expand(_stdin); + auto tokens = lex(view); + if (error_count()) return print_errors(_stdout), error(); + + TokenView tview(tokens, src, true); + Value program = parse(tview); + if (error_count()) return print_errors(_stdout), error(); + + prep(global, program); + Value result = eval(global, program); + if (error_count()) return print_errors(_stdout), error(); + + if (!result.is_runtime()) { + if (!result.is_void()) + println(BOLDBLUE, "= ", result, RESET, "\n"); + return result; + } + if (_print_ast) + println(BOLDCYAN, result.get_runtime(), RESET, "\n"); + + jasmine::Object object; + generate(result, mainfn); + compile(result, object, mainfn); + if (error_count()) return print_errors(_stdout), error(); + + print(BOLDBLUE); + jit_print(result, object); + println(RESET); + return result; + } + + ref load(Source& src) { + auto view = src.begin(); + auto tokens = lex(view); + if (error_count()) return print_errors(_stdout), ref::null(); + + TokenView tview(tokens, src); + Value program = parse(tview); + if (error_count()) return print_errors(_stdout), ref::null(); + + ref global = create_global_env(); + + prep(global, program); + Value result = eval(global, program); + if (error_count()) return print_errors(_stdout), ref::null(); + + return global; + } + + int run(Source& src) { + auto view = src.begin(); + auto tokens = lex(view); + if (error_count()) return print_errors(_stdout), 1; + + TokenView tview(tokens, src); + Value program = parse(tview); + if (error_count()) return print_errors(_stdout), 1; + + ref global = create_global_env(); + + prep(global, program); + Value result = eval(global, program); + if (error_count()) return print_errors(_stdout), 1; + + if (!result.is_runtime()) return 0; + if (_print_ast) + println(BOLDCYAN, result.get_runtime(), RESET, "\n"); + + jasmine::Object object; + compile(result, object); + if (error_count()) return print_errors(_stdout), 1; + + return execute(result, object); + } +} \ No newline at end of file diff --git a/driver.h b/driver.h new file mode 100644 index 0000000..095e67b --- /dev/null +++ b/driver.h @@ -0,0 +1,21 @@ +#ifndef BASIL_DRIVER_H +#define BASIL_DRIVER_H + +#include "source.h" +#include "env.h" +#include "ssa.h" +#include "values.h" + +namespace basil { + void print_tokens(bool should); + void print_parsed(bool should); + void print_ast(bool should); + void print_ssa(bool should); + void print_asm(bool should); + + Value repl(ref global, Source& src, Function& mainfn); + ref load(Source& src); + int run(Source& src); +} + +#endif \ No newline at end of file diff --git a/env.cpp b/env.cpp index 7deef36..dbc8e2f 100644 --- a/env.cpp +++ b/env.cpp @@ -29,7 +29,7 @@ namespace basil { } Env::Env(const ref& parent): - _parent(parent) {} + _parent(parent), _runtime(false) {} void Env::def(const string& name) { _defs[name] = Def(false, false, false); @@ -107,6 +107,27 @@ namespace basil { Env new_env(_parent); ref new_ref(new_env); for (auto& p : _defs) new_ref->_defs.put(p.first, p.second); - return new_env; + new_ref->_runtime = _runtime; + return new_ref; } + + map::const_iterator Env::begin() const { + return _defs.begin(); + } + + map::const_iterator Env::end() const { + return _defs.end(); + } + + void Env::import(ref env) { + for (auto& p : env->_defs) _defs.put(p.first, p.second); + } + + void Env::make_runtime() { + _runtime = true; + } + + bool Env::is_runtime() const { + return _runtime; + } } \ No newline at end of file diff --git a/env.h b/env.h index 4c944c4..e4519d2 100644 --- a/env.h +++ b/env.h @@ -1,11 +1,12 @@ #ifndef BASIL_ENV_H #define BASIL_ENV_H -#include "defs.h" -#include "hash.h" -#include "str.h" -#include "rc.h" +#include "util/defs.h" +#include "util/hash.h" +#include "util/str.h" +#include "util/rc.h" #include "values.h" +#include "ssa.h" namespace basil { struct Def { @@ -15,6 +16,7 @@ namespace basil { bool is_proc; // is the definition a scalar or procedure? u8 arity; // number of arguments taken by a procedure. u8 precedence; // precedence of infix procedure + Location location; Def(bool is_macro_in = false, bool is_procedure_in = false, bool is_infix_in = false, u8 arity_in = 0, u8 precedence_in = 0); @@ -30,6 +32,7 @@ namespace basil { class Env { map _defs; ref _parent; + bool _runtime; public: Env(const ref& parent = ref::null()); @@ -49,6 +52,11 @@ namespace basil { Def* find(const string& name); void format(stream& io) const; ref clone() const; + map::const_iterator begin() const; + map::const_iterator end() const; + void import(ref env); + void make_runtime(); + bool is_runtime() const; }; } diff --git a/errors.cpp b/errors.cpp index fe58033..9b13d8a 100644 --- a/errors.cpp +++ b/errors.cpp @@ -1,5 +1,5 @@ #include "errors.h" -#include "vec.h" +#include "util/vec.h" #include "lex.h" namespace basil { @@ -20,9 +20,10 @@ namespace basil { }; static vector errors; + static i64 silenced = 0; void err(SourceLocation loc, const string& message) { - errors.push({ loc, message }); + if (!silenced) errors.push({ loc, message }); } u32 error_count() { @@ -39,6 +40,7 @@ namespace basil { write(io, "[", e.loc.line + 1, ":", e.loc.column + 1, "] "); writeln(io, e.message); } + clear_errors(); } void print_errors(stream& io, const Source& src) { @@ -59,4 +61,13 @@ namespace basil { writeln(io, ""); } } + + void silence_errors() { + silenced ++; + } + + void unsilence_errors() { + silenced --; + if (silenced < 0) silenced = 0; + } } \ No newline at end of file diff --git a/errors.h b/errors.h index 68775c4..c42e557 100644 --- a/errors.h +++ b/errors.h @@ -1,10 +1,10 @@ #ifndef BASIL_ERRORS_H #define BASIL_ERRORS_H -#include "defs.h" -#include "str.h" -#include "io.h" -#include "slice.h" +#include "util/defs.h" +#include "util/str.h" +#include "util/io.h" +#include "util/slice.h" namespace basil { class Token; @@ -28,6 +28,8 @@ namespace basil { void clear_errors(); void print_errors(stream& io); void print_errors(stream& io, const Source& src); + void silence_errors(); + void unsilence_errors(); template void err(SourceLocation loc, Args... args) { diff --git a/eval.cpp b/eval.cpp index 63e5554..e23680d 100644 --- a/eval.cpp +++ b/eval.cpp @@ -1,4 +1,7 @@ #include "eval.h" +#include "source.h" +#include "ast.h" +#include "driver.h" namespace basil { Value builtin_add(ref env, const Value& args) { @@ -61,6 +64,10 @@ namespace basil { return greater_equal(args.get_product()[0], args.get_product()[1]); } + Value builtin_is_empty(ref env, const Value& args) { + return is_empty(args.get_product()[0]); + } + Value builtin_head(ref env, const Value& args) { return head(args.get_product()[0]); } @@ -73,28 +80,86 @@ namespace basil { return cons(args.get_product()[0], args.get_product()[1]); } + Value builtin_display(ref env, const Value& args) { + return display(args.get_product()[0]); + } + + Value gen_assign(ref env, const Value& dest, const Value& src) { + return list_of(Value("#="), list_of(Value("quote"), dest), src); + } + + Value builtin_assign_macro(ref env, const Value& args) { + return gen_assign(env, args.get_product()[0], + args.get_product()[1]); + } + + Value builtin_assign(ref env, const Value& args) { + return assign(env, args.get_product()[0], args.get_product()[1]); + } + + Value builtin_read_line(ref env, const Value& args) { + return read_line(); + } + + Value builtin_if_macro(ref env, const Value& args) { + return list_of(Value("#?"), args.get_product()[0], + list_of(Value("quote"), args.get_product()[1]), + list_of(Value("quote"), args.get_product()[2])); + } + + Value builtin_if(ref env, const Value& args) { + Value cond = args.get_product()[0]; + + if (cond.is_runtime()) { + Value left = eval(env, args.get_product()[1]), + right = eval(env, args.get_product()[2]); + if (left.is_error() || right.is_error()) return error(); + if (!left.is_runtime()) left = lower(left); + if (!right.is_runtime()) right = lower(right); + ASTNode* ln = left.get_runtime(); + ASTNode* rn = right.get_runtime(); + return new ASTIf(cond.loc(), cond.get_runtime(), ln, rn); + } + + if (!cond.is_bool()) { + err(cond.loc(), "Expected boolean condition in if ", + "expression, given '", cond.type(), "'."); + return error(); + } + + if (cond.get_bool()) return eval(env, args.get_product()[1]); + else return eval(env, args.get_product()[2]); + } + ref create_root_env() { ref root; root->def("nil", Value(VOID)); - root->infix("+", Value(new FunctionValue(root, builtin_add, 2)), 2, 20); - root->infix("-", Value(new FunctionValue(root, builtin_sub, 2)), 2, 20); - root->infix("*", Value(new FunctionValue(root, builtin_mul, 2)), 2, 40); - root->infix("/", Value(new FunctionValue(root, builtin_div, 2)), 2, 40); - root->infix("%", Value(new FunctionValue(root, builtin_rem, 2)), 2, 40); - root->infix("and", Value(new FunctionValue(root, builtin_and, 2)), 2, 5); - root->infix("or", Value(new FunctionValue(root, builtin_or, 2)), 2, 5); - root->infix("xor", Value(new FunctionValue(root, builtin_xor, 2)), 2, 5); - root->def("not", Value(new FunctionValue(root, builtin_not, 1)), 1); - root->infix("==", Value(new FunctionValue(root, builtin_equal, 2)), 2, 10); - root->infix("!=", Value(new FunctionValue(root, builtin_inequal, 2)), 2, 10); - root->infix("<", Value(new FunctionValue(root, builtin_less, 2)), 2, 10); - root->infix(">", Value(new FunctionValue(root, builtin_greater, 2)), 2, 10); - root->infix("<=", Value(new FunctionValue(root, builtin_less_equal, 2)), 2, 10); - root->infix(">=", Value(new FunctionValue(root, builtin_greater_equal, 2)), 2, 10); - root->infix("head", Value(new FunctionValue(root, builtin_head, 1)), 1, 80); - root->infix("tail", Value(new FunctionValue(root, builtin_tail, 1)), 1, 80); - root->infix("::", Value(new FunctionValue(root, builtin_cons, 2)), 2, 15); - root->infix("cons", Value(new FunctionValue(root, builtin_cons, 2)), 2, 15); + root->infix("+", new FunctionValue(root, builtin_add, 2), 2, 20); + root->infix("-", new FunctionValue(root, builtin_sub, 2), 2, 20); + root->infix("*", new FunctionValue(root, builtin_mul, 2), 2, 40); + root->infix("/", new FunctionValue(root, builtin_div, 2), 2, 40); + root->infix("%", new FunctionValue(root, builtin_rem, 2), 2, 40); + root->infix("and", new FunctionValue(root, builtin_and, 2), 2, 5); + root->infix("or", new FunctionValue(root, builtin_or, 2), 2, 5); + root->infix("xor", new FunctionValue(root, builtin_xor, 2), 2, 5); + root->def("not", new FunctionValue(root, builtin_not, 1), 1); + root->infix("==", new FunctionValue(root, builtin_equal, 2), 2, 10); + root->infix("!=", new FunctionValue(root, builtin_inequal, 2), 2, 10); + root->infix("<", new FunctionValue(root, builtin_less, 2), 2, 10); + root->infix(">", new FunctionValue(root, builtin_greater, 2), 2, 10); + root->infix("<=", new FunctionValue(root, builtin_less_equal, 2), 2, 10); + root->infix(">=", new FunctionValue(root, builtin_greater_equal, 2), 2, 10); + root->infix("empty?", new FunctionValue(root, builtin_is_empty, 1), 1, 60); + root->infix("head", new FunctionValue(root, builtin_head, 1), 1, 80); + root->infix("tail", new FunctionValue(root, builtin_tail, 1), 1, 80); + root->infix("::", new FunctionValue(root, builtin_cons, 2), 2, 15); + root->infix("cons", new FunctionValue(root, builtin_cons, 2), 2, 15); + root->def("display", new FunctionValue(root, builtin_display, 1), 1); + root->infix_macro("=", new MacroValue(root, builtin_assign_macro, 2), 2, 0); + root->infix("#=", new FunctionValue(root, builtin_assign, 2), 2, 0); + root->infix_macro("?", new MacroValue(root, builtin_if_macro, 3), 3, 2); + root->infix("#?", new FunctionValue(root, builtin_if, 3), 3, 2); + root->def("read-line", new FunctionValue(root, builtin_read_line, 0), 0); root->def("true", Value(true, BOOL)); root->def("false", Value(false, BOOL)); return root; @@ -104,19 +169,11 @@ namespace basil { Value eval_list(ref env, const Value& list); Value eval(ref env, Value term); + Value infix(ref env, const Value& term, bool is_macro); + Value define(ref env, const Value& term, bool is_macro); // utilities - vector to_vector(const Value& list) { - vector values; - const Value* v = &list; - while (v->is_list()) { - values.push(v->get_list().head()); - v = &v->get_list().tail(); - } - return values; - } - vector to_ptr_vector(const Value& list) { vector values; const Value* v = &list; @@ -142,34 +199,41 @@ namespace basil { const Value& h = head(list); if (!h.is_symbol()) return false; const string& name = symbol_for(h.get_symbol()); - if (name == "def" || name == "macro") { - const Value& second = tail(list); - if (second.is_list()) { - const Value& third = tail(second); - - // if list has more than three elements and third parameter - // is a list (aka is a procedure-type definition) - if (third.is_list() && tail(third).is_list() && - !head(third).is_symbol()) return true; - } + if (name == "def") { + return tail(list).is_list() && + head(tail(list)).is_list(); // is procedure } - else if (name == "lambda") return true; + else if (name == "lambda" || name == "infix" + || name == "infix-macro" || name == "macro") return true; return false; } + static i64 traverse_deep = 0; + + void enable_deep() { + traverse_deep ++; + } + + void disable_deep() { + traverse_deep --; + if (traverse_deep < 0) traverse_deep = 0; + } + void traverse_list(ref env, const Value& list, void(*fn)(ref, const Value&)) { vector vals = to_ptr_vector(list); - u32 length = introduces_env(list) ? vals.size() - 1 : vals.size(); - for (u32 i = 0; i < length; i ++) fn(env, *vals[i]); + if (!introduces_env(list) || traverse_deep) + for (u32 i = 0; i < vals.size(); i ++) + fn(env, *vals[i]); } void traverse_list(ref env, Value& list, void(*fn)(ref, Value&)) { vector vals = to_ptr_vector(list); - u32 length = introduces_env(list) ? vals.size() - 1 : vals.size(); - for (u32 i = 0; i < length; i ++) fn(env, *vals[i]); + if (!introduces_env(list) || traverse_deep) + for (u32 i = 0; i < vals.size(); i ++) + fn(env, *vals[i]); } void handle_splice(ref env, Value& item) { @@ -178,25 +242,65 @@ namespace basil { if (h.is_symbol() && h.get_symbol() == symbol_value("splice")) { Value t = tail(item); if (t.is_void()) item = Value(VOID); - else item = eval(env, head(tail(item))); + else { + Value t = tail(item); + prep(env, t); + item = eval(env, t); + } } else traverse_list(env, item, handle_splice); } + Value use(ref env, const Value& term); + + void handle_use(ref env, Value& item) { + if (!item.is_list()) return; + Value h = head(item); + if (h.is_symbol() && h.get_symbol() == symbol_value("use")) { + use(env, item); + item = list_of(string("list-of")); + } + else traverse_list(env, item, handle_use); + } + void visit_macro_defs(ref env, const Value& item) { if (!item.is_list()) return; Value h = head(item); - if (h.is_symbol() && h.get_symbol() == symbol_value("macro")) { - vector values = to_vector(item); - if (values.size() < 2 || !values[1].is_symbol()) - err(item.loc(), "Expected name in definition."); + if (h.is_symbol() && + (h.get_symbol() == symbol_value("macro") + || h.get_symbol() == symbol_value("infix-macro"))) { + bool infix = h.get_symbol() == symbol_value("infix-macro"); + u8 precedence = 0; + vector values = to_vector(item); + u32 i = 1; + if (values.size() >= 2 && infix && values[i].is_int()) { + precedence = u8(values[i].get_int()); + i ++; + } + if (i + 1 >= values.size() || + (!values[i].is_symbol() && + !(values[i].is_list() && head(values[i]).is_symbol()) && + !(values[i].is_list() && tail(values[i]).is_list() && + head(tail(values[i])).is_symbol()))) { + err(item.loc(), "Expected variable or function name ", + "in definition."); + return; + } if (values.size() < 3) err(item.loc(), "Expected value in definition."); - if (values.size() > 3) { // procedure - env->def_macro(symbol_for(values[1].get_symbol()), - to_vector(values[2]).size()); + if (values[i].is_list()) { // procedure + if (infix) { + Value rest = tail(values[i]); + if (!rest.is_list()) { + err(rest.loc(), "Infix function must take at least one ", + "argument."); + return; + } + basil::infix(env, item, true); + } + else define(env, item, true); } - else env->def_macro(symbol_for(values[1].get_symbol())); + else define(env, item, true); } else traverse_list(env, item, visit_macro_defs); } @@ -204,24 +308,68 @@ namespace basil { void visit_defs(ref env, const Value& item) { if (!item.is_list()) return; Value h = head(item); - if (h.is_symbol() && h.get_symbol() == symbol_value("def")) { - vector values = to_vector(item); - if (values.size() < 2 || !values[1].is_symbol()) - err(item.loc(), "Expected name in definition."); + if (h.is_symbol() && + (h.get_symbol() == symbol_value("def") + || h.get_symbol() == symbol_value("infix"))) { + bool infix = h.get_symbol() == symbol_value("infix"); + u8 precedence = 0; + vector values = to_vector(item); + u32 i = 1; + if (values.size() >= 2 && infix && values[i].is_int()) { + precedence = u8(values[i].get_int()); + i ++; + } + if (i + 1 >= values.size() || + (!values[i].is_symbol() && + !(values[i].is_list() && head(values[i]).is_symbol()) && + !(values[i].is_list() && tail(values[i]).is_list() && + head(tail(values[i])).is_symbol()))) { + err(item.loc(), "Expected variable or function name ", + "in definition."); + return; + } if (values.size() < 3) err(item.loc(), "Expected value in definition."); - if (values.size() > 3) { // procedure - env->def(symbol_for(values[1].get_symbol()), - to_vector(values[2]).size()); + if (values[i].is_list()) { // procedure + if (infix) { + Value rest = tail(values[i]); + if (!rest.is_list()) { + err(rest.loc(), "Infix function must take at least one ", + "argument."); + return; + } + const string& name = symbol_for(head(rest).get_symbol()); + // if (env->find(name)) { + // err(values[i].loc(), "Redefinition of '", name, "'."); + // return; + // } + env->infix(name, to_vector(tail(rest)).size() + 1, + precedence); + } + else { + const string& name = symbol_for(head(values[i]).get_symbol()); + // if (env->find(name)) { + // err(values[i].loc(), "Redefinition of '", name, "'."); + // return; + // } + env->def(name, to_vector(tail(values[i])).size()); + } } - else env->def(symbol_for(values[1].get_symbol())); + else { + const string& name = symbol_for(values[i].get_symbol()); + // if (env->find(name)) { + // err(values[i].loc(), "Redefinition of '", name, "'."); + // return; + // } + env->def(name); + } } else traverse_list(env, item, visit_defs); } void handle_macro(ref env, Value& item); - Value expand(const Value& macro, const Value& arg) { + Value expand(ref env, const Value& macro, const Value& arg) { if (!macro.is_macro() && !macro.is_error()) { err(macro.loc(), "Expanded value is not a macro."); return error(); @@ -234,7 +382,7 @@ namespace basil { const MacroValue& fn = macro.get_macro(); if (fn.is_builtin()) { - return fn.get_builtin()(fn.get_env(), arg); + return fn.get_builtin()(env, arg); } else { ref env = fn.get_env(); @@ -244,30 +392,52 @@ namespace basil { argc, " provided."); return error(); } - for (u32 i = 0; i < arity; i ++) { - const string& argname = symbol_for(fn.args()[i]); - env->find(argname)->value = arg.get_product()[i]; + for (u32 i = 0; i < arity; i ++) { + if (fn.args()[i] & KEYWORD_ARG_BIT) { + // keyword arg + Value argument = eval(env, arg.get_product()[i]); + if (!argument.is_symbol() || + argument.get_symbol() != + (fn.args()[i] & ARG_NAME_MASK)) { + err(arg.get_product()[i].loc(), "Expected keyword '", + symbol_for(fn.args()[i] & ARG_NAME_MASK), "', got '", + argument, "'."); + return error(); + } + } + else { + const string& argname = symbol_for(fn.args()[i] & ARG_NAME_MASK); + env->find(argname)->value = arg.get_product()[i]; + } } Value result = fn.body().clone(); - handle_splice(fn.get_env(), result); - return result; + enable_deep(); + handle_splice(env, result); + disable_deep(); + prep(env, result); + return result; } } void handle_macro(ref env, Value& item) { if (item.is_symbol()) { const Def* def = env->find(symbol_for(item.get_symbol())); - if (def && def->is_macro_variable()) + if (def && def->is_macro_variable()) { item = def->value.get_alias().value(); + return; + } } else if (item.is_list()) { const Value& h = head(item); if (h.is_symbol()) { const Def* def = env->find(symbol_for(h.get_symbol())); - if (def && def->is_macro_procedure()) + if (def && def->is_macro_procedure()) { item = eval_list(env, item); + return; + } } } + traverse_list(env, item, handle_macro); } Value apply_op(const Value& op, const Value& lhs) { @@ -278,10 +448,68 @@ namespace basil { return list_of(op, lhs, rhs); } + Value apply_op(const Value& op, const Value& lhs, const vector& internals, + const Value& rhs) { + Value l = list_of(rhs); + for (i64 i = i64(internals.size()) - 1; i >= 0; i --) { + l = cons(internals[i], l); + } + return cons(op, cons(lhs, l)); + } + + pair unary_helper(ref env, const Value& lhs, + const Value& term); + pair infix_helper(ref env, const Value& lhs, + const Value& op, const Def* def, const Value& rhs, const Value& term, + const vector& internals); + pair infix_helper(ref env, const Value& lhs, + const Value& op, const Def* def, const Value& term); + pair infix_transform(ref env, const Value& term); + + pair infix_helper(ref env, const Value& lhs, + const Value& op, const Def* def, const Value& rhs, const Value& term, + const vector& internals) { + Value iter = term; + + if (iter.is_void()) + return { apply_op(op, lhs, internals, rhs), iter }; + Value next_op = head(iter); + if (!next_op.is_symbol()) + return { apply_op(op, lhs, internals, rhs), iter }; + const Def* next_def = env->find(symbol_for(next_op.get_symbol())); + if (!next_def || !next_def->is_infix) + return { apply_op(op, lhs, internals, rhs), iter }; + iter = tail(iter); // consume op + + if (next_def->precedence > def->precedence) { + if (next_def->arity == 1) { + return infix_helper(env, lhs, op, def, + apply_op(next_op, rhs), iter, internals); + } + auto p = infix_helper(env, rhs, next_op, next_def, iter); + return { apply_op(op, lhs, internals, p.first), p.second }; + } + else { + Value result = apply_op(op, lhs, rhs); + if (next_def->arity == 1) { + return unary_helper(env, apply_op(next_op, result), iter); + } + return infix_helper(env, apply_op(op, lhs, internals, rhs), + next_op, next_def, iter); + } + } + pair infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& term) { Value iter = term; + vector internals; + if (def->arity > 2) for (u32 i = 0; i < def->arity - 2; i ++) { + auto p = infix_transform(env, iter); + internals.push(p.first); + iter = p.second; + } + if (iter.is_void()) { err(term.loc(), "Expected term in binary expression."); return { error(), term }; @@ -289,18 +517,23 @@ namespace basil { Value rhs = head(iter); iter = tail(iter); // consume second term - if (iter.is_void()) return { apply_op(op, lhs, rhs), iter }; - Value next_op = head(iter); - if (!next_op.is_symbol()) return { apply_op(op, lhs, rhs), iter }; - const Def* next_def = env->find(symbol_for(next_op.get_symbol())); - if (!next_def || !next_def->is_infix) return { apply_op(op, lhs, rhs), iter }; + return infix_helper(env, lhs, op, def, rhs, iter, internals); + } + + pair unary_helper(ref env, const Value& lhs, + const Value& term) { + Value iter = term; + + if (iter.is_void()) return { lhs, iter }; + Value op = head(iter); + if (!op.is_symbol()) return { lhs, iter }; + const Def* def = env->find(symbol_for(op.get_symbol())); + if (!def || !def->is_infix) return { lhs, iter }; iter = tail(iter); // consume op - - if (next_def->precedence > def->precedence) { - auto p = infix_helper(env, rhs, next_op, next_def, iter); - return { apply_op(op, lhs, p.first), p.second }; - } - else return infix_helper(env, apply_op(op, lhs, rhs), next_op, next_def, iter); + + if (def->arity == 1) + return unary_helper(env, apply_op(op, lhs), iter); + return infix_helper(env, lhs, op, def, iter); } pair infix_transform(ref env, const Value& term) { @@ -320,8 +553,8 @@ namespace basil { if (!def || !def->is_infix) return { lhs, iter }; iter = tail(iter); // consume op - // todo: unary op - + if (def->arity == 1) + return unary_helper(env, apply_op(op, lhs), iter); return infix_helper(env, lhs, op, def, iter); } @@ -337,53 +570,127 @@ namespace basil { return result; } - // definition stuff + void apply_infix(ref env, Value& item); + + void apply_infix_at(ref env, Value& item, u32 depth) { + Value* iter = &item; + while (depth && iter->is_list()) { + iter = &iter->get_list().tail(); + depth --; + } + *iter = handle_infix(env, *iter); + traverse_list(env, *iter, apply_infix); + } + + void apply_infix(ref env, Value& item) { + Value orig = item.clone(); + if (!item.is_list()) return; + Value h = head(item); + if (h.is_symbol()) { + const string& name = symbol_for(h.get_symbol()); + if (name == "macro" || name == "infix" + || name == "infix-macro" || name == "lambda" + || name == "quote" || name == "splice") return; + else if (name == "def") { + if (tail(item).is_list() && head(tail(item)).is_symbol()) + apply_infix_at(env, item, 2); + } + else if (name == "if" || name == "do" || name == "list-of") + apply_infix_at(env, item, 1); + else { + silence_errors(); + Value proc = eval(env, h); + unsilence_errors(); + if (proc.is_function()) apply_infix_at(env, item, 1); + else apply_infix_at(env, item, 0); + } + } + else apply_infix_at(env, item, 0); + } + + void prep(ref env, Value& term) { + handle_splice(env, term); + handle_use(env, term); + visit_macro_defs(env, term); + handle_macro(env, term); + visit_defs(env, term); + apply_infix(env, term); + handle_macro(env, term); + visit_defs(env, term); + } + // definition stuff Value define(ref env, const Value& term, bool is_macro) { vector values = to_vector(term); - if (values.size() == 3) { // variable - if (is_macro) - env->def_macro(symbol_for(values[1].get_symbol()), - Value(new AliasValue(values[2]))); - else - env->def(symbol_for(values[1].get_symbol()), - eval(env, values[2])); - return Value(VOID); + + // visit_defs already does some error-checking, so we + // don't need to check the number of values or their types + // exhaustively. + + if (values[1].is_symbol()) { // variable + const string& name = symbol_for(values[1].get_symbol()); + + if (is_macro) { + env->def_macro(name, Value(new AliasValue(values[2]))); + return Value(VOID); + } + else { + Value init = eval(env, values[2]); + if (env->is_runtime()) + init = lower(init); + env->def(name, init); + if (init.is_runtime()) + return new ASTDefine(values[0].loc(), env, + values[1].get_symbol(), init.get_runtime()); + return Value(VOID); + } } - else { - if (!values[2].is_list()) { - err(values[2].loc(), "Expected argument list in function."); - return error(); - } - Env env_descendant(env); + else if (values[1].is_list()) { // procedure + const string& name = symbol_for(head(values[1]).get_symbol()); + + Env env_descendant(env); ref function_env(env_descendant); - vector args = to_vector(values[2]); + vector args = to_vector(tail(values[1])); vector argnames; for (const Value& v : args) { if (v.is_symbol()) { argnames.push(v.get_symbol()); function_env->def(symbol_for(v.get_symbol())); } + else if (v.is_list() && head(v).is_symbol() + && symbol_for(head(v).get_symbol()) == "quote") { + argnames.push(eval(env, v).get_symbol() | KEYWORD_ARG_BIT); + } + else { + err(v.loc(), "Only symbols and quoted symbols are permitted ", + "within an argument list."); + return error(); + } } vector body; - for (u32 i = 3; i < values.size(); i ++) body.push(values[i]); + for (u32 i = 2; i < values.size(); i ++) body.push(values[i]); + Value body_term = cons(Value("do"), list_of(body)); if (is_macro) { - Value mac(new MacroValue(function_env, - argnames, cons(Value("do"), list_of(body)))); - env->def_macro(symbol_for(values[1].get_symbol()), - mac, argnames.size()); + Value mac(new MacroValue(function_env, argnames, body_term)); + env->def_macro(name, mac, argnames.size()); } else { - Value func(new FunctionValue(function_env, - argnames, cons(Value("do"), handle_infix(env, list_of(body))))); - env->def(symbol_for(values[1].get_symbol()), - func, argnames.size()); + prep(function_env, body_term); + Value func(new FunctionValue(function_env, argnames, body_term, + symbol_value(name))); + env->def(name, func, argnames.size()); } return Value(VOID); } + else { + err(values[1].loc(), "First parameter to definition must be ", + "a symbol (for variable or alias) or list (for procedure ", + "or macro)."); + return error(); + } } - Value infix(ref env, const Value& term) { + Value infix(ref env, const Value& term, bool is_macro) { vector values = to_vector(term); u8 precedence = 0; @@ -393,45 +700,74 @@ namespace basil { precedence = (u8)values[i].get_int(); i ++; } - if (!values[i].is_symbol()) { - err(values[i].loc(), "Expected name for infix definition."); - return error(); - } - const string& name = symbol_for(values[i].get_symbol()); - i ++; - if (i + 1 >= values.size()) { err(values[0].loc(), "Expected argument list and body in ", "infix definition."); return error(); } if (!values[i].is_list()) { + err(values[i].loc(), "Expected name and argument list for ", + "infix definition."); + return error(); + } + if (!head(tail(values[i])).is_symbol()) { + err(tail(values[i]).loc(), "Expected name for infix definition."); + return error(); + } + const string& name = symbol_for(head(tail(values[i])).get_symbol()); + + vector args; + args.push(head(values[i])); + Value v = tail(tail(values[i])); + while (v.is_list()) { + args.push(head(v)); + v = tail(v); + } + if (args.size() == 0) { err(values[i].loc(), "Expected argument list in infix ", "definition."); return error(); } + i ++; Env env_descendant(env); ref function_env(env_descendant); - vector args = to_vector(values[i]); - i ++; vector argnames; for (const Value& v : args) { if (v.is_symbol()) { argnames.push(v.get_symbol()); function_env->def(symbol_for(v.get_symbol())); } + else if (v.is_list() && head(v).is_symbol() + && symbol_for(head(v).get_symbol()) == "quote" + && head(tail(v)).is_symbol()) { + argnames.push(eval(env, v).get_symbol() | KEYWORD_ARG_BIT); + } + else { + err(v.loc(), "Only symbols and quoted symbols are permitted ", + "within an argument list."); + return error(); + } } vector body; for (; i < values.size(); i ++) body.push(values[i]); - Value func(new FunctionValue(function_env, - argnames, cons(Value("do"), handle_infix(env, list_of(body))))); - env->infix(name, func, argnames.size(), precedence); - return Value(VOID); + Value body_term = cons(Value("do"), list_of(body)); + if (is_macro) { + Value mac(new MacroValue(function_env, argnames, body_term)); + env->infix_macro(name, mac, argnames.size(), precedence); + return Value(VOID); + } + else { + prep(function_env, body_term); + Value func(new FunctionValue(function_env, argnames, body_term, + symbol_value(name))); + env->infix(name, func, argnames.size(), precedence); + return Value(VOID); + } } Value lambda(ref env, const Value& term) { vector values = to_vector(term); - if (values.size() < 2) { + if (values.size() < 3) { err(values[0].loc(), "Expected argument list and body in ", "lambda expression."); return error(); @@ -450,64 +786,237 @@ namespace basil { argnames.push(v.get_symbol()); function_env->def(symbol_for(v.get_symbol())); } + else { + err(v.loc(), "Only symbols are permitted ", + "within a lambda's argument list."); + return error(); + } } vector body; for (u32 i = 2; i < values.size(); i ++) body.push(values[i]); + Value body_term = list_of(body); + prep(function_env, body_term); return Value(new FunctionValue(function_env, argnames, - cons(Value("do"), handle_infix(env, list_of(body))))); + cons(Value("do"), body_term))); } Value do_block(ref env, const Value& term) { const Value& todo = tail(term); const Value* v = &todo; + vector values; + bool runtime = false; while (v->is_list()) { - if (v->get_list().tail().is_void()) // last element - return eval(env, v->get_list().head()); - eval(env, v->get_list().head()); + values.push(eval(env, v->get_list().head())); + if (values.back().is_runtime()) runtime = true; v = &v->get_list().tail(); } - return Value(VOID); + if (values.size() == 0) return Value(VOID); + if (runtime) { + vector nodes; + for (Value& v : values) if (!v.is_runtime()) { + v = lower(v); + if (v.is_error()) return v; + } + for (Value& v : values) nodes.push(v.get_runtime()); + return new ASTBlock(term.loc(), nodes); + } + return values.back(); } + u64 get_keyword(const Value& v) { + if (v.is_symbol()) return v.get_symbol(); + else if (v.is_list() && head(v).is_symbol() + && head(v).get_symbol() == symbol_value("quote") + && tail(v).is_list() && head(tail(v)).is_symbol()) + return head(tail(v)).get_symbol(); + return 0; + } + + bool is_keyword(const Value& v, const string& word) { + if (v.is_symbol()) return v.get_symbol() == symbol_value(word); + else if (v.is_list() && head(v).is_symbol() + && head(v).get_symbol() == symbol_value("quote") + && tail(v).is_list() && head(tail(v)).is_symbol()) + return head(tail(v)).get_symbol() == symbol_value(word); + return false; + } + Value if_expr(ref env, const Value& term) { - Value handled = cons(head(term), handle_infix(env, tail(term))); - vector values = to_vector(handled); - if (values.size() != 4) { - err(term.loc(), "Incorrect number of arguments for if expression. ", - "Expected condition, case if true, and case if false."); + Value params = tail(term); + prep(env, params); + Value cond, if_true, if_false; + bool has_else = false; + vector if_true_vals, if_false_vals; + if (!params.is_list()) { + err(term.loc(), "Expected condition in if expression."); + return error(); + } + cond = head(params); + params = tail(params); + while (params.is_list() && !(is_keyword(head(params), "else") + || is_keyword(head(params), "elif"))) { + if_true_vals.push(head(params)); + params = tail(params); + } + if_true = cons(Value("do"), list_of(if_true_vals)); + if (!params.is_list()) { + err(term.loc(), "If expression requires at least one else."); + return error(); + } + if (get_keyword(head(params)) == symbol_value("elif")) { + if_false = if_expr(env, params); + return list_of(Value("#?"), + cond, + list_of(Value("quote"), if_true), + list_of(Value("quote"), if_false)); + } + params = tail(params); + while (params.is_list()) { + if_false_vals.push(head(params)); + params = tail(params); + } + if_false = cons(Value("do"), list_of(if_false_vals)); + return list_of(Value("#?"), + cond, + list_of(Value("quote"), if_true), + list_of(Value("quote"), if_false)); + } + + bool is_quoted_symbol(const Value& val) { + return val.is_list() && tail(val).is_list() + && head(val).is_symbol() && head(val).get_symbol() == symbol_value("quote") + && head(tail(val)).is_symbol(); + } + + void find_assigns(ref env, const Value& term, + set& dests) { + if (!term.is_list()) return; + Value h = head(term); + if (h.is_symbol() && h.get_symbol() == symbol_value("#=")) { + if (tail(term).is_list() && is_quoted_symbol(head(tail(term)))) { + u64 sym = eval(env, head(tail(term))).get_symbol(); + Def* def = env->find(symbol_for(sym)); + if (def) dests.insert(sym); + } + } + if (!introduces_env(term)) { + const Value* v = &term; + while (v->is_list()) { + find_assigns(env, v->get_list().head(), dests); + v = &v->get_list().tail(); + } + } + } + + Value while_stmt(ref env, const Value& term) { + vector values = to_vector(term); + if (values.size() < 3) { + err(term.loc(), "Incorrect number of arguments for while ", + "statement. Expected condition and body."); return error(); } - Value cond = eval(env, values[1]); - if (!cond.is_bool()) { - err(cond.loc(), "Expected boolean condition in if expression, given '", - cond.type(), "'."); - return error(); + + Value body = cons(Value("do"), tail(tail(term))); + + set dests; + find_assigns(env, head(tail(term)), dests); + find_assigns(env, body, dests); + + vector nodes; + for (u64 u : dests) { + Def* def = env->find(symbol_for(u)); + if (!def->value.is_runtime()) { + Value new_value = lower(def->value); + nodes.push( + new ASTDefine(new_value.loc(), env, u, new_value.get_runtime())); + def->value = new_value; + } + } + + Value cond = eval(env, head(tail(term))); + if (cond.is_error()) return error(); + if (!cond.is_runtime()) cond = lower(cond); + + body = eval(env, body); + if (body.is_error()) return error(); + if (!body.is_runtime()) body = lower(body); + nodes.push(new ASTWhile(term.loc(), cond.get_runtime(), + body.get_runtime())); + return nodes.size() == 1 ? nodes[0] : new ASTBlock(term.loc(), nodes); + } + + Value list_of(ref env, const Value& term) { + Value items = tail(term); + + const Value* v = &items; + vector vals; + while (v->is_list()) { + vals.push(eval(env, v->get_list().head())); + v = &v->get_list().tail(); } - if (cond.get_bool()) return eval(env, values[2]); - else return eval(env, values[3]); + return list_of(vals); } + Value use(ref env, const Value& term) { + if (!tail(term).is_list() || !head(tail(term)).is_symbol()) { + err(tail(term).loc(), "Expected symbol in use expression, given '", + tail(term), "'."); + return error(); + } + Value h = head(tail(term)); + string path = symbol_for(h.get_symbol()); + path += ".bl"; + + Source src((const char*)path.raw()); + if (!src.begin().peek()) { + err(h.loc(), "Could not load source file at path '", path, "'."); + return error(); + } + ref module = load(src); + if (error_count()) return error(); + + for (const auto& p : *module) { + if (env->find(p.first)) { + err(term.loc(), "Module '", symbol_for(h.get_symbol()), + "' redefines '", p.first, "' in the current environment."); + return error(); + } + } + env->import(module); + return Value(VOID); + } + Value eval_list(ref env, const Value& term) { Value h = head(term); if (h.is_symbol()) { const string& name = symbol_for(h.get_symbol()); - if (name == "quote") return head(tail(term)); + if (name == "quote") return head(tail(term)).clone(); else if (name == "def") return define(env, term, false); - else if (name == "infix") return infix(env, term); + else if (name == "infix") return infix(env, term, false); else if (name == "macro") return define(env, term, true); + else if (name == "infix-macro") return infix(env, term, true); else if (name == "lambda") return lambda(env, term); else if (name == "do") return do_block(env, term); - else if (name == "if") return if_expr(env, term); + else if (name == "if") return eval(env, if_expr(env, term)); + else if (name == "list-of") return list_of(env, term); + else if (name == "use") return use(env, term); + else if (name == "while") return while_stmt(env, term); } Value first = eval(env, h); if (first.is_macro()) { vector args; const Value* v = &term.get_list().tail(); + u32 i = 0; while (v->is_list()) { - args.push(v->get_list().head()); + if (i < first.get_macro().arity() + && first.get_macro().args()[i] & KEYWORD_ARG_BIT + && v->get_list().head().is_symbol()) + args.push(list_of(Value("quote"), v->get_list().head())); + else args.push(v->get_list().head()); v = &v->get_list().tail(); + i ++; } if (args.size() != first.get_macro().arity()) { err(term.loc(), "Macro procedure expects ", @@ -515,15 +1024,21 @@ namespace basil { args.size(), " provided."); return error(); } - return expand(first, Value(new ProductValue(args))); + return expand(env, first, Value(new ProductValue(args))); } else if (first.is_function()) { vector args; - Value args_term = handle_infix(env, tail(term)); + Value args_term = tail(term); const Value* v = &args_term; + u32 i = 0; while (v->is_list()) { - args.push(eval(env, v->get_list().head())); + if (i < first.get_function().arity() + && first.get_function().args()[i] & KEYWORD_ARG_BIT + && v->get_list().head().is_symbol()) + args.push(v->get_list().head()); // leave keywords quoted + else args.push(eval(env, v->get_list().head())); v = &v->get_list().tail(); + i ++; } if (args.size() != first.get_function().arity()) { err(term.loc(), "Procedure expects ", @@ -531,37 +1046,35 @@ namespace basil { args.size(), " provided."); return error(); } - return call(first, Value(new ProductValue(args))); + return call(env, first, Value(new ProductValue(args))); } - if (tail(term).is_void()) return eval(env, h); + if (tail(term).is_void()) return first; - Value infix_term = handle_infix(env, term); - if (infix_term == term) { - err(term.loc(), "Could not evaluate list."); - return error(); - } - else return eval_list(env, infix_term); + err(term.loc(), "Could not evaluate list."); + return error(); } Value eval(ref env, Value term) { - handle_splice(env, term); - visit_macro_defs(env, term); - handle_macro(env, term); - visit_defs(env, term); if (term.is_list()) return eval_list(env, term); else if (term.is_int()) return term; + else if (term.is_string()) return term; else if (term.is_symbol()) { const string& name = symbol_for(term.get_symbol()); const Def* def = env->find(name); if (def && def->is_macro_variable()) return def->value.get_alias().value(); - else if (def) return def->value; + else if (def) { + if (def->value.is_runtime()) + return new ASTVar(term.loc(), env, term.get_symbol()); + return def->value; + } else { err(term.loc(), "Undefined variable '", name, "'."); return error(); } } + err(term.loc(), "Could not evaluate term '", term, "'."); return error(); } } \ No newline at end of file diff --git a/eval.h b/eval.h index 7cde8d1..ca3b4e6 100644 --- a/eval.h +++ b/eval.h @@ -1,13 +1,16 @@ #ifndef BASIL_EVAL_H #define BASIL_EVAL_H -#include "defs.h" +#include "util/defs.h" #include "values.h" #include "env.h" namespace basil { ref create_root_env(); + bool introduces_env(const Value& list); + + void prep(ref env, Value& term); Value eval(ref env, Value term); } diff --git a/jasmine/obj.cpp b/jasmine/obj.cpp new file mode 100644 index 0000000..9077bdb --- /dev/null +++ b/jasmine/obj.cpp @@ -0,0 +1,268 @@ +#include "obj.h" +#include "sym.h" +#include "target.h" +#include +#include +#include + +namespace jasmine { + Object::Object(Architecture architecture): + arch(architecture), loaded_code(nullptr) { + // + } + + Object::Object(const char* path, Architecture architecture): Object(architecture) { + read(path); + } + + Object::~Object() { + if (loaded_code) free_exec(loaded_code, buf.size()); + } + + byte_buffer& Object::code() { + return buf; + } + + u64 Object::size() const { + return buf.size(); + } + + void Object::define(Symbol symbol) { + defs.put(symbol, buf.size()); + } + + void Object::reference(Symbol symbol, RefType type, i8 field_offset) { + refs[buf.size()] = { symbol, type, field_offset }; + } + + void Object::resolve_refs() { + for (auto& ref : refs) { + u8* pos = (u8*)loaded_code + ref.first; + u8* sym = (u8*)find(ref.second.symbol); + if (!sym) { + fprintf(stderr, "[ERROR] Could not resolve ref '%s'.\n", + name(ref.second.symbol)); + exit(1); + } + u8* field = pos + ref.second.field_offset; + switch (ref.second.type) { + case REL8: + *(i8*)field = i8(sym - pos); + break; + case REL16_LE: + *(i16*)field = little_endian(sym - pos); + break; + case REL16_BE: + *(i16*)field = big_endian(sym - pos); + break; + case REL32_LE: + *(i32*)field = little_endian(sym - pos); + break; + case REL32_BE: + *(i32*)field = big_endian(sym - pos); + break; + case REL64_LE: + *(i64*)field = little_endian(sym - pos); + break; + case REL64_BE: + *(i64*)field = big_endian(sym - pos); + break; + case ABS8: + *(i8*)field = i8(i64(sym)); + break; + case ABS16_LE: + *(i16*)field = little_endian(i64(sym)); + break; + case ABS16_BE: + *(i16*)field = big_endian(i64(sym)); + break; + case ABS32_LE: + *(i32*)field = little_endian(i64(sym)); + break; + case ABS32_BE: + *(i32*)field = big_endian(i64(sym)); + break; + case ABS64_LE: + *(i64*)field = little_endian(i64(sym)); + break; + case ABS64_BE: + *(i64*)field = big_endian(i64(sym)); + break; + default: + break; + } + } + } + + void Object::load() { + loaded_code = alloc_exec(buf.size()); + u8* writer = (u8*)loaded_code; + byte_buffer code_copy = buf; + while (code_copy.size()) *writer++ = code_copy.read(); + + resolve_refs(); + + protect_exec(loaded_code, buf.size()); + } + + void Object::write(const char* path) { + FILE* file = fopen(path, "w"); + if (!file) { + fprintf(stderr, "[ERROR] Could not open file '%s'.\n", path); + exit(1); + } + + byte_buffer b; + b.write("#!jasmine\n", 10); + + b.write(JASMINE_VERSION); // version major + b.write(arch); // architecture + b.write("\xf0\x9f\xa6\x9d", 4); // friendly flamingo + + b.write(little_endian(buf.size())); // length of code + byte_buffer code_copy = buf; + while (code_copy.size()) b.write(code_copy.read()); // copy over code + + u32 internal_id = 0; + map internal_syms; + vector sym_order; + for (auto& p : defs) { + auto it = internal_syms.find(p.first); + if (it == internal_syms.end()) { + internal_syms.put(p.first, internal_id ++); + sym_order.push(p.first); + } + } + for (auto& p : refs) { + auto it = internal_syms.find(p.second.symbol); + if (it == internal_syms.end()) { + internal_syms.put(p.second.symbol, internal_id ++); + sym_order.push(p.second.symbol); + } + } + + b.write(little_endian(internal_syms.size())); // number of symbols used in object + for (auto& sym : sym_order) { + b.write((u8)sym.type); // symbol type + const char* str = name(sym); + b.write(str, strlen(str) + 1); // symbol name, null-terminated + } + + b.write(little_endian(defs.size())); // number of defs + for (auto& p : defs) { + b.write(little_endian(p.second)); // offset + b.write(internal_syms[p.first]); // symbol id + } + + b.write(little_endian(refs.size())); // number of refs + for (auto& p : refs) { + b.write(little_endian(p.first)); // offset + b.write(p.second.type); // ref type + b.write(p.second.field_offset); // field offset + b.write(internal_syms[p.second.symbol]); // symbol id + } + + while (b.size()) fputc(b.read(), file); + fclose(file); + } + + void Object::read(const char* path) { + FILE* file = fopen(path, "r"); + if (!file) { + fprintf(stderr, "[ERROR] Could not open file '%s'.\n", path); + exit(1); + } + char symbol[1024]; + + byte_buffer b; + int ch; + while ((ch = fgetc(file)) != EOF) b.write(ch); + u8 shebang[10]; + for (int i = 0; i < 10; i ++) shebang[i] = b.read(); + if (strncmp((const char*)shebang, "#!jasmine\n", 10)) { + fprintf(stderr, "[ERROR] Incorrect shebang.\n"); + exit(1); + } + + u8 version = b.read(); // get version + arch = (Architecture)b.read(); // get arch + + u8 magic[4]; + for (int i = 0; i < 4; i ++) magic[i] = b.read(); + if (strncmp((const char*)magic, "\xf0\x9f\xa6\x9d", 4)) { + fprintf(stderr, "[ERROR] Incorrect magic number.\n"); + exit(1); + } + + u64 code_length = from_little_endian(b.read()); // code length + while (code_length) { + if (!b.size()) { + fprintf(stderr, "[ERROR] File contains less code than announced.\n"); + exit(1); + } + buf.write(b.read()); // read code from file + -- code_length; + } + + u64 sym_count = from_little_endian(b.read()); + vector internal_syms; + while (sym_count) { + SymbolLinkage type = (SymbolLinkage)b.read(); + + u64 size = 0; + while (b.peek()) { + if (size > 1023) { + fprintf(stderr, "[ERROR] Encountered symbol longer than 1024 characters.\n"); + exit(1); + } + symbol[size ++] = b.read(); + } + symbol[size ++] = b.read(); + + internal_syms.push(type == GLOBAL_SYMBOL ? global(symbol) : local(symbol)); + -- sym_count; + } + + u64 def_count = from_little_endian(b.read()); + while (def_count) { + if (!b.size()) { + fprintf(stderr, "[ERROR] File contains fewer symbol defs than announced.\n"); + exit(1); + } + + u64 offset = from_little_endian(b.read()); + Symbol sym = internal_syms[from_little_endian(b.read())]; + + defs.put(sym, offset); + -- def_count; + } + + u64 ref_count = from_little_endian(b.read()); + while (ref_count) { + if (!b.size()) { + fprintf(stderr, "[ERROR] File contains fewer symbol refs than announced.\n"); + exit(1); + } + + u64 offset = from_little_endian(b.read()); + RefType type = (RefType)b.read(); + i8 field_offset = b.read(); + Symbol sym = internal_syms[from_little_endian(b.read())]; + + refs.put(offset, { sym, type, field_offset }); + -- ref_count; + } + fclose(file); + } + + Architecture Object::architecture() const { + return arch; + } + + void* Object::find(Symbol symbol) const { + if (!loaded_code) return nullptr; + auto it = defs.find(symbol); + if (it == defs.end()) return nullptr; + return (u8*)loaded_code + it->second; + } +} \ No newline at end of file diff --git a/jasmine/obj.h b/jasmine/obj.h new file mode 100644 index 0000000..7aa0e1f --- /dev/null +++ b/jasmine/obj.h @@ -0,0 +1,55 @@ +#ifndef JASMINE_OBJECT_H +#define JASMINE_OBJECT_H + +#include "utils.h" +#include "target.h" +#include "sym.h" + +namespace jasmine { + enum RefType : u8 { + REL8, REL16_LE, REL32_LE, REL64_LE, // relative reference, e.g. for jumps or rip-relative addressing + REL16_BE, REL32_BE, REL64_BE, + ABS8, ABS16_LE, ABS32_LE, ABS64_LE, // absolute reference + ABS16_BE, ABS32_BE, ABS64_BE + }; + + struct SymbolRef { + Symbol symbol; + RefType type; + i8 field_offset; + }; + + class Object { + Architecture arch; + byte_buffer buf; + map defs; + map refs; + void* loaded_code; + + void resolve_refs(); + public: + Object(Architecture architecture = DEFAULT_ARCH); + Object(const char* path, Architecture architecture = DEFAULT_ARCH); + ~Object(); + Object(const Object&) = delete; + Object& operator=(const Object&) = delete; + + byte_buffer& code(); + u64 size() const; + void define(Symbol symbol); + void reference(Symbol symbol, RefType type, i8 field_offset); + void load(); + void write(const char* path); + void read(const char* path); + Architecture architecture() const; + + void* find(Symbol symbol) const; + + template + T* find(Symbol symbol) const { + return (T*)Object::find(symbol); + } + }; +} + +#endif \ No newline at end of file diff --git a/jasmine/sym.cpp b/jasmine/sym.cpp new file mode 100644 index 0000000..8e15aa6 --- /dev/null +++ b/jasmine/sym.cpp @@ -0,0 +1,42 @@ +#include "sym.h" +#include +#include "util/str.h" + +namespace jasmine { + struct SymbolTable { + vector symbol_names; + map symbols; + + SymbolTable() {} + ~SymbolTable() { + // + } + + Symbol enter(const string& name, SymbolLinkage linkage) { + auto it = symbols.find(name); + if (it == symbols.end()) { + symbol_names.push(name); + return symbols[name] = { symbol_names.size() - 1, linkage }; + } + else return it->second; + } + }; + + static SymbolTable table; + + bool operator==(Symbol a, Symbol b) { + return a.id == b.id; + } + + Symbol global(const char* name) { + return table.enter(name, GLOBAL_SYMBOL); + } + + Symbol local(const char* name) { + return table.enter(name, LOCAL_SYMBOL); + } + + const char* name(Symbol symbol) { + return (const char*)table.symbol_names[symbol.id].raw(); + } +} \ No newline at end of file diff --git a/jasmine/sym.h b/jasmine/sym.h new file mode 100644 index 0000000..efcef4d --- /dev/null +++ b/jasmine/sym.h @@ -0,0 +1,24 @@ +#ifndef JASMINE_SYMBOL_H +#define JASMINE_SYMBOL_H + +#include "utils.h" +#include + +namespace jasmine { + enum SymbolLinkage : u8 { + GLOBAL_SYMBOL, LOCAL_SYMBOL + }; + + struct Symbol { + u32 id; + SymbolLinkage type; + }; + + bool operator==(Symbol a, Symbol b); + + Symbol global(const char* name); + Symbol local(const char* name); + const char* name(Symbol symbol); +} + +#endif \ No newline at end of file diff --git a/jasmine/target.h b/jasmine/target.h new file mode 100644 index 0000000..c255f7c --- /dev/null +++ b/jasmine/target.h @@ -0,0 +1,24 @@ +#ifndef JASMINE_VERSION_H +#define JASMINE_VERSION_H + +#include "utils.h" + +const u8 JASMINE_VERSION = 1; + +enum Architecture : u8 { + UNSUPPORTED = 0, + X86_64 = 1, + AMD64 = 1, + X86 = 2, + AARCH64 = 3 +}; + +#if defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) + const Architecture DEFAULT_ARCH = X86_64; +#elif defined(__i386) || defined(__i386__) || defined(__x86) || defined(__x86__) + const Architecture DEFAULT_ARCH = X86; +#elif defined(__aarch64) || defined(__aarch64__) + const Architecture DEFAULT_ARCH = AARCH64; +#endif + +#endif \ No newline at end of file diff --git a/jasmine/utils.cpp b/jasmine/utils.cpp new file mode 100644 index 0000000..cfe8d4c --- /dev/null +++ b/jasmine/utils.cpp @@ -0,0 +1,117 @@ +#include "utils.h" +#include + +byte_buffer::byte_buffer(): + _start(0), _end(0), _capacity(32), _data(new u8[_capacity]) { + // starts with empty (uninitialized) buffer of 32 bytes +} + +byte_buffer::~byte_buffer() { + delete[] _data; +} + +byte_buffer::byte_buffer(const byte_buffer& other): + _start(other._start), _end(other._end), + _capacity(other._capacity), _data(new u8[_capacity]) { + + // copies memory from start to end + for (u64 i = _start; i != _end; i = (i + 1) & (_capacity - 1)) + _data[i] = other._data[i]; +} + +byte_buffer& byte_buffer::operator=(const byte_buffer& other) { + if (this != &other) { + // free existing allocation + delete[] _data; + + // copy from other + _start = other._start; + _end = other._end; + _capacity = other._capacity; + _data = new u8[_capacity]; + for (u64 i = _start; i != _end; i = (i + 1) & (_capacity - 1)) + _data[i] = other._data[i]; + } + return *this; +} + +u8 byte_buffer::peek() const { + // empty buffer returns null char + if (_start == _end) return '\0'; + + // return next byte + return _data[_start]; +} + +u8 byte_buffer::read() { + // empty buffer returns null char + if (_start == _end) return '\0'; + + // read next byte + u8 byte = _data[_start]; + _start = (_start + 1) & (_capacity - 1); + return byte; +} + +void byte_buffer::read(char* buffer, u64 length) { + for (u64 i = 0; i < length; i ++) + buffer[i] = read(); +} + +void byte_buffer::write(u8 byte) { + u64 new_end = (_end + 1) & (_capacity - 1); + if (new_end == _start) { + // hold onto old buffer + u64 old_start = _start, old_end = _end, old_capacity = _capacity; + u8* old_data = _data; + + // grow and clear buffer + _start = 0; + _end = 0; + _capacity *= 2; + _data = new u8[_capacity]; + + // copy from old buffer + while (old_start != old_end) { + write(old_data[old_start]); + old_start = (old_start + 1) & (old_capacity - 1); + } + + // free old buffer + delete[] old_data; + } + + _data[_end] = byte; + _end = (_end + 1) & (_capacity - 1); +} + +void byte_buffer::write(const char* string, u64 length) { + for (u64 i = 0; i < length; i ++) + write((u8)string[i]); +} + +u64 byte_buffer::size() const { + u64 end = _end; + if (end < _start) end += _capacity; // account for wraparound + return end - _start; +} + +void byte_buffer::clear() { + _start = _end; +} + +#if defined(__APPLE__) || defined(__linux__) + #include "sys/mman.h" + + void* alloc_exec(u64 size) { + return mmap(nullptr, size, PROT_READ | PROT_EXEC | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + } + + void protect_exec(void* exec, u64 size) { + mprotect(exec, size, PROT_READ | PROT_EXEC); + } + + void free_exec(void* exec, u64 size) { + munmap(exec, size); + } +#endif \ No newline at end of file diff --git a/jasmine/utils.h b/jasmine/utils.h new file mode 100644 index 0000000..b41a084 --- /dev/null +++ b/jasmine/utils.h @@ -0,0 +1,111 @@ +#ifndef JASMINE_UTIL_H +#define JASMINE_UTIL_H + +#include + +#include "../util/defs.h" +#include "../util/vec.h" +#include "../util/hash.h" + +enum class EndianOrder : u32 { + UTIL_LITTLE_ENDIAN = 0x03020100ul, + UTIL_BIG_ENDIAN = 0x00010203ul, + UTIL_PDP_ENDIAN = 0x01000302ul, + UTIL_HONEYWELL_ENDIAN = 0x02030001ul +}; + +static const union { + u8 bytes[4]; + u32 value; +} host_order = { { 0, 1, 2, 3 } }; + +template +T flip_endian(T value) { + T result = 0; + constexpr const i64 bits = sizeof(T) * 8; + for (i64 i = bits - 8; i >= 0; i -= 8) { + result |= ((value >> i) & 0xff) << ((bits - 8) - i); + } + return result; +} + +template +T little_endian(T value) { + if ((EndianOrder)host_order.value == EndianOrder::UTIL_LITTLE_ENDIAN) + return value; + return flip_endian(value); +} + +template +T big_endian(T value) { + if ((EndianOrder)host_order.value == EndianOrder::UTIL_BIG_ENDIAN) + return value; + return flip_endian(value); +} + +template +T from_big_endian(T value) { + if ((EndianOrder)host_order.value == EndianOrder::UTIL_BIG_ENDIAN) + return value; + return flip_endian(value); +} + +template +T from_little_endian(T value) { + if ((EndianOrder)host_order.value == EndianOrder::UTIL_LITTLE_ENDIAN) + return value; + return flip_endian(value); +} + +class byte_buffer { + u64 _start; + u64 _end; + u64 _capacity; + u8* _data; +public: + byte_buffer(); + ~byte_buffer(); + byte_buffer(const byte_buffer& other); + byte_buffer& operator=(const byte_buffer& other); + + u8 peek() const; + u8 read(); + void read(char* buffer, u64 length); + void write(u8 byte); + void write(const char* string, u64 length); + u64 size() const; + void clear(); + + template + T read() { + // serializes object from lowest to highest address + static u8 buffer[sizeof(T)]; + for (u32 i = 0; i < sizeof(T); i ++) + buffer[i] = byte_buffer::read(); + return *(T*)buffer; + } + + template + void write(const T& value) { + // deserializes object, assuming first byte is lowest address + const u8* data = (const u8*)&value; + for (u32 i = 0; i < sizeof(T); i ++) write(data[i]); + } + + template + void write(u8 b, Args... args) { + write(b); + write(args...); + } +}; + +// allocates writable, executable memory +void* alloc_exec(u64 size); + +// protects executable memory from being written +void protect_exec(void* exec, u64 size); + +// deallocates executable memory +void free_exec(void* exec, u64 size); + +#endif \ No newline at end of file diff --git a/jasmine/x64.cpp b/jasmine/x64.cpp new file mode 100644 index 0000000..eaae5ca --- /dev/null +++ b/jasmine/x64.cpp @@ -0,0 +1,1244 @@ +#include "x64.h" +#include +#include + +namespace x64 { + using namespace jasmine; + + static const char* register_names[] = { + "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" + }; + + static const char* size_names[] = { + "byte", "word", "dword", "qword", "auto" + }; + + // the object machine code is written to + static Object* target = nullptr; + + void writeto(Object& buf) { + target = &buf; + } + + ScaledRegister::ScaledRegister(Register reg_in, Scale scale_in): + reg(reg_in), scale(scale_in) { + // + } + + ScaledRegister operator*(Register reg, int num) { + Scale scale = SCALE1; + switch (num) { + case 1: + scale = SCALE1; + break; + case 2: + scale = SCALE2; + break; + case 4: + scale = SCALE4; + break; + case 8: + scale = SCALE8; + break; + default: + fprintf(stderr, "[ERROR] Unknown scale factor '%d'.\n", num); + exit(1); + } + return ScaledRegister{ reg, scale }; + } + + bool is_register(ArgType type) { + return type < 4; + } + + bool is_immediate(ArgType type) { + return (type >= 4 && type < 7) || type == IMM_AUTO; + } + + bool is_memory(ArgType type) { + return type >= REGISTER_LABEL8 && type < IMM_AUTO; + } + + bool is_label(ArgType type) { + return (type >= LABEL8 && type <= LABEL64) || type == LABEL_AUTO; + } + + bool is_absolute(ArgType type) { + return (type >= ABSOLUTE8 && type <= ABSOLUTE64) || type == ABSOLUTE_AUTO; + } + + bool is_rip_relative(ArgType type) { + return (type >= RIPRELATIVE8 && type <= RIPRELATIVE64) || type == RIPRELATIVE_AUTO; + } + + bool is_displacement_only(ArgType type) { + return is_absolute(type) || is_rip_relative(type); + } + + Size operand_size(ArgType type) { + if (type >= IMM_AUTO) return AUTO; + return (Size)((u8)type & 3); + } + + Register base_register(const Arg& arg) { + switch (arg.type) { + case REGISTER_LABEL8: + case REGISTER_LABEL16: + case REGISTER_LABEL32: + case REGISTER_LABEL64: + case REGISTER_LABEL_AUTO: + return arg.data.register_label.base; + case REGISTER_OFFSET8: + case REGISTER_OFFSET16: + case REGISTER_OFFSET32: + case REGISTER_OFFSET64: + case REGISTER_OFFSET_AUTO: + return arg.data.register_offset.base; + case REGISTER8: + case REGISTER16: + case REGISTER32: + case REGISTER64: + return arg.data.reg; + case SCALED_INDEX8: + case SCALED_INDEX16: + case SCALED_INDEX32: + case SCALED_INDEX64: + case SCALED_INDEX_AUTO: + return arg.data.scaled_index.base; + default: + return INVALID; + } + } + + bool is_64bit_register(Register r) { + return r >= R8 && r <= R15; + } + + i64 memory_displacement(const Arg& arg) { + switch (arg.type) { + case REGISTER_OFFSET8: + case REGISTER_OFFSET16: + case REGISTER_OFFSET32: + case REGISTER_OFFSET64: + case REGISTER_OFFSET_AUTO: + return arg.data.register_offset.offset; + case SCALED_INDEX8: + case SCALED_INDEX16: + case SCALED_INDEX32: + case SCALED_INDEX64: + case SCALED_INDEX_AUTO: + return arg.data.scaled_index.offset; + case ABSOLUTE8: + case ABSOLUTE16: + case ABSOLUTE32: + case ABSOLUTE64: + case ABSOLUTE_AUTO: + return arg.data.absolute; + case RIPRELATIVE8: + case RIPRELATIVE16: + case RIPRELATIVE32: + case RIPRELATIVE64: + case RIPRELATIVE_AUTO: + return arg.data.rip_relative; + default: + return 0; + } + } + + i64 immediate_value(const Arg& arg) { + switch (arg.type) { + case IMM8: + return arg.data.imm8; + case IMM16: + return arg.data.imm16; + case IMM32: + return arg.data.imm32; + case IMM64: + case IMM_AUTO: + return arg.data.imm64; + default: + return 0; + } + } + + bool is_scaled_addressing(ArgType type) { + return (type >= SCALED_INDEX8 && type <= SCALED_INDEX64) + || type == SCALED_INDEX_AUTO; + } + + RefType relative(Size size) { + switch (size) { + case BYTE: return REL8; + case WORD: return REL16_LE; + case DWORD: return REL32_LE; + case QWORD: return REL64_LE; + default: { + fprintf(stderr, "[ERROR] Invalid size for relative reference.\n"); + exit(1); + } + } + } + + RefType absolute(Size size) { + switch (size) { + case BYTE: return ABS8; + case WORD: return ABS16_LE; + case DWORD: return ABS32_LE; + case QWORD: return ABS64_LE; + default: { + fprintf(stderr, "[ERROR] Invalid size for absolute reference.\n"); + exit(1); + } + } + } + + Arg imm8(i8 value) { + Arg arg; + arg.data.imm8 = value; + arg.type = IMM8; + return arg; + } + + Arg imm16(i16 value) { + Arg arg; + arg.data.imm16 = value; + arg.type = IMM16; + return arg; + } + + Arg imm32(i32 value) { + Arg arg; + arg.data.imm32 = value; + arg.type = IMM32; + return arg; + } + + Arg imm64(i64 value) { + Arg arg; + arg.data.imm64 = value; + arg.type = IMM64; + return arg; + } + + Arg imm(i64 value) { + Arg arg; + arg.data.imm64 = value; + arg.type = IMM_AUTO; + return arg; + } + + Arg r8(Register reg) { + Arg arg; + arg.data.reg = reg; + arg.type = REGISTER8; + return arg; + } + + Arg r16(Register reg) { + Arg arg; + arg.data.reg = reg; + arg.type = REGISTER16; + return arg; + } + + Arg r32(Register reg) { + Arg arg; + arg.data.reg = reg; + arg.type = REGISTER32; + return arg; + } + + Arg r64(Register reg) { + Arg arg; + arg.data.reg = reg; + arg.type = REGISTER64; + return arg; + } + + Arg m8(Register reg, i64 offset) { + Arg arg; + arg.data.register_offset = { reg, offset }; + arg.type = REGISTER_OFFSET8; + return arg; + } + + Arg m16(Register reg, i64 offset) { + Arg arg; + arg.data.register_offset = { reg, offset }; + arg.type = REGISTER_OFFSET16; + return arg; + } + + Arg m32(Register reg, i64 offset) { + Arg arg; + arg.data.register_offset = { reg, offset }; + arg.type = REGISTER_OFFSET32; + return arg; + } + + Arg m64(Register reg, i64 offset) { + Arg arg; + arg.data.register_offset = { reg, offset }; + arg.type = REGISTER_OFFSET64; + return arg; + } + + Arg mem(Register reg, i64 offset) { + Arg arg; + arg.data.register_offset = { reg, offset }; + arg.type = REGISTER_OFFSET_AUTO; + return arg; + } + + Arg m8(Register base, Register index, Scale scale, i64 offset) { + Arg arg; + arg.data.scaled_index = { base, index, scale, offset }; + arg.type = SCALED_INDEX8; + return arg; + } + + Arg m16(Register base, Register index, Scale scale, i64 offset) { + Arg arg; + arg.data.scaled_index = { base, index, scale, offset }; + arg.type = SCALED_INDEX16; + return arg; + } + + Arg m32(Register base, Register index, Scale scale, i64 offset) { + Arg arg; + arg.data.scaled_index = { base, index, scale, offset }; + arg.type = SCALED_INDEX32; + return arg; + } + + Arg m64(Register base, Register index, Scale scale, i64 offset) { + Arg arg; + arg.data.scaled_index = { base, index, scale, offset }; + arg.type = SCALED_INDEX64; + return arg; + } + + Arg mem(Register base, Register index, Scale scale, i64 offset) { + Arg arg; + arg.data.scaled_index = { base, index, scale, offset }; + arg.type = SCALED_INDEX_AUTO; + return arg; + } + + Arg m8(Register base, ScaledRegister index, i64 offset) { + return m8(base, index.reg, index.scale, offset); + } + + Arg m16(Register base, ScaledRegister index, i64 offset) { + return m16(base, index.reg, index.scale, offset); + } + + Arg m32(Register base, ScaledRegister index, i64 offset) { + return m32(base, index.reg, index.scale, offset); + } + + Arg m64(Register base, ScaledRegister index, i64 offset) { + return m64(base, index.reg, index.scale, offset); + } + + Arg mem(Register base, ScaledRegister index, i64 offset) { + return mem(base, index.reg, index.scale, offset); + } + + Arg abs8(i64 offset) { + Arg arg; + arg.data.absolute = offset; + arg.type = ABSOLUTE8; + return arg; + } + + Arg abs16(i64 offset) { + Arg arg; + arg.data.absolute = offset; + arg.type = ABSOLUTE16; + return arg; + } + + Arg abs32(i64 offset) { + Arg arg; + arg.data.absolute = offset; + arg.type = ABSOLUTE32; + return arg; + } + + Arg abs64(i64 offset) { + Arg arg; + arg.data.absolute = offset; + arg.type = ABSOLUTE64; + return arg; + } + + Arg abs(i64 offset) { + Arg arg; + arg.data.absolute = offset; + arg.type = ABSOLUTE_AUTO; + return arg; + } + + Arg riprel8(i64 offset) { + Arg arg; + arg.data.rip_relative = offset; + arg.type = RIPRELATIVE8; + return arg; + } + + Arg riprel16(i64 offset) { + Arg arg; + arg.data.rip_relative = offset; + arg.type = RIPRELATIVE16; + return arg; + } + + Arg riprel32(i64 offset) { + Arg arg; + arg.data.rip_relative = offset; + arg.type = RIPRELATIVE32; + return arg; + } + + Arg riprel64(i64 offset) { + Arg arg; + arg.data.rip_relative = offset; + arg.type = RIPRELATIVE64; + return arg; + } + + Arg riprel(i64 offset) { + Arg arg; + arg.data.rip_relative = offset; + arg.type = RIPRELATIVE_AUTO; + return arg; + } + + Arg label8(jasmine::Symbol symbol) { + Arg arg; + arg.data.label = symbol; + arg.type = LABEL8; + return arg; + } + + Arg label16(jasmine::Symbol symbol) { + Arg arg; + arg.data.label = symbol; + arg.type = LABEL16; + return arg; + } + + Arg label32(jasmine::Symbol symbol) { + Arg arg; + arg.data.label = symbol; + arg.type = LABEL32; + return arg; + } + + Arg label64(jasmine::Symbol symbol) { + Arg arg; + arg.data.label = symbol; + arg.type = LABEL64; + return arg; + } + + void verify_buffer() { + if (!target) { // requires that a buffer exist to write to + fprintf(stderr, "[ERROR] Cannot assemble; no target buffer set.\n"); + exit(1); + } + } + + void verify_args(const Arg& dest, const Arg& src) { + if (is_memory(dest.type) && is_memory(src.type)) { + // multiple memory parameters is illegal + fprintf(stderr, "[ERROR] More than one memory parameter in instruction.\n"); + exit(1); + } + if (is_immediate(dest.type)) { + // cannot write to immediate + fprintf(stderr, "[ERROR] Destination parameter cannot be immediate.\n"); + exit(1); + } + } + + Size resolve_size(const Arg& src, Size target) { + Size src_size = operand_size(src.type); + if (target == AUTO) { + if (src_size == AUTO) { + fprintf(stderr, "[ERROR] Ambiguous size for instruction.\n"); + exit(1); + } + return src_size; + } + else if (src_size != target && src_size != AUTO) { + // src is incompatible with explicit instruction size + fprintf(stderr, "[ERROR] Incompatible size; source has size '%s'" + ", but instruction has size '%s'.", size_names[src_size], + size_names[target]); + exit(1); + } + + return target; // return explicit size if no inference occurred + } + + Size resolve_size(const Arg& dest, const Arg& src, Size target) { + Size dest_size = operand_size(dest.type); + Size src_size = operand_size(src.type); + + if (target == AUTO) { + if (dest_size == AUTO && src_size == AUTO) { + fprintf(stderr, "[ERROR] Ambiguous size for instruction.\n"); + exit(1); + } + else if (dest_size == AUTO) { // infer size from source + return src_size; + } + else if (src_size == AUTO) { // infer size from destination + return dest_size; + } + else if (dest_size != src_size) { // incompatible sizes + fprintf(stderr, "[ERROR] Incompatible operand sizes; destination has " + "size '%s', but source has size '%s'.\n", + size_names[dest_size], size_names[src_size]); + exit(1); + } + return dest_size; // dest and source size must be equal and non-auto + } + else if (dest_size != target && dest_size != AUTO) { + // dest is incompatible with explicit instruction size + fprintf(stderr, "[ERROR] Incompatible size; destination has size '%s'" + ", but instruction has size '%s'.\n", size_names[dest_size], + size_names[target]); + exit(1); + } + else if (src_size != target && src_size != AUTO) { + // src is incompatible with explicit instruction size + fprintf(stderr, "[ERROR] Incompatible size; source has size '%s'" + ", but instruction has size '%s'.", size_names[src_size], + size_names[target]); + exit(1); + } + + return target; // return explicit size if no inference occurred + } + + void emitprefix(const Arg& dest, Size size) { + + } + + void emitprefix(const Arg& dest, const Arg& src, Size size) { + if (size == WORD) target->code().write(0x66); // 16-bit prefix + u8 rex = 0x40; + Register src_reg = base_register(src); + Register dest_reg = base_register(dest); + + if (is_64bit_register(dest_reg)) rex |= 1; // 64-bit r/m field + + if (is_scaled_addressing(dest.type) && + is_64bit_register(dest.data.scaled_index.index)) rex |= 2; // 64-bit SIB index + if (is_scaled_addressing(src.type) && + is_64bit_register(src.data.scaled_index.index)) rex |= 2; // 64-bit SIB index + + if (is_64bit_register(src_reg)) rex |= 4; // 64-bit reg field + + if (size == QWORD) rex |= 8; // 64-bit operand size + if (rex > 0x40) target->code().write(rex); + } + + void write_immediate(const Arg& src, Size size, bool allow64 = false) { + switch (size) { + case BYTE: + target->code().write(src.data.imm8); + break; + case WORD: + target->code().write(little_endian(src.data.imm16)); + break; + case DWORD: + target->code().write(little_endian(src.data.imm32)); + break; + case QWORD: + if (allow64) { + target->code().write(little_endian(src.data.imm64)); + break; + } + if ((src.data.imm64 > 0 ? src.data.imm64 : -src.data.imm64) + & ~0xffffffff) { + fprintf(stderr, "[ERROR] Cannot represent immediate in 32-bits.\n"); + exit(1); + } + target->code().write(little_endian((i32)src.data.imm64)); + break; + default: + fprintf(stderr, "[ERROR] Source operand size cannot be determined.\n"); + exit(1); + } + } + + void write_immediate(i64 imm, Size size, bool allow64 = false) { + switch (size) { + case BYTE: + target->code().write(imm); + break; + case WORD: + target->code().write(little_endian(imm)); + break; + case DWORD: + target->code().write(little_endian(imm)); + break; + case QWORD: + if (allow64) { + target->code().write(little_endian(imm)); + break; + } + if ((imm > 0 ? imm : -imm) + & ~0xffffffff) { + fprintf(stderr, "[ERROR] Cannot represent immediate in 32-bits.\n"); + exit(1); + } + target->code().write(little_endian(imm)); + break; + default: + fprintf(stderr, "[ERROR] Source operand size cannot be determined.\n"); + exit(1); + } + } + + void emitargs(const Arg& dest, const Arg& src, Size size, i8 imod = -1) { + Size dest_size = operand_size(dest.type), src_size = operand_size(src.type); + if (dest_size == AUTO) dest_size = size; + if (src_size == AUTO) src_size = size; + + // Mod R/M byte + // 7 - - - 5 - - - - - 2 - - - - - 0 + // Mod Reg R/M + // + // Mod = 11 when operation is register/register + // Mod = 00 when operation is register/memory or memory/register + // Reg = source register (base register), or 000 if immediate + // R/M = destination register (base register) + u8 modrm = 0; + + // SIB byte + // 7 - - - 5 - - - - - 2 - - - - - 0 + // Scale Index Base + // + // Scale can be any x64::Scale + // Index and Base are registers + // Specifies an address of the form (Base + Index * Scale + Offset) + // When Index, Base = RSP, represents RSP-relative addressing + u8 sib = 0; + bool has_sib = false; + + // offset/displacement + i64 disp = 0; + + if (is_memory(dest.type)) { + disp = memory_displacement(dest); + + if (is_absolute(src.type)) { + sib |= RSP << 3; + sib |= RBP; + has_sib = true; + } + else if (is_scaled_addressing(dest.type)) { + sib |= dest.data.scaled_index.scale << 6; + sib |= dest.data.scaled_index.index << 3; + sib |= dest.data.scaled_index.base; + has_sib = true; + } + else if (base_register(dest) == RSP) { + sib |= RSP << 3; + sib |= RSP; + has_sib = true; + } + } + else if (is_memory(src.type)) { + disp = memory_displacement(src); + + if (is_absolute(src.type)) { + sib |= RSP << 3; + sib |= RBP; + has_sib = true; + } + else if (is_scaled_addressing(src.type)) { + sib |= src.data.scaled_index.scale << 6; + sib |= src.data.scaled_index.index << 3; + sib |= src.data.scaled_index.base; + has_sib = true; + } + else if (base_register(src) == RSP) { + sib |= RSP << 3; + sib |= RSP; + has_sib = true; + } + } + else modrm |= 0b11000000; // register-register mod + + if (disp && !is_displacement_only(src.type) && + !is_displacement_only(dest.type)) { + if (disp > -129 && disp < 128) modrm |= 0b01000000; // 8-bit offset + else if (disp < -0x80000000l || disp > 0x7fffffffl) { + fprintf(stderr, "[ERROR] Cannot represent memory offset %lx " + "in 32 bits.\n", disp); + exit(1); + } + else modrm |= 0b10000000; // 32-bit offset + } + + if (imod != -1) modrm |= imod << 3; // opcode extension in reg + else if (is_scaled_addressing(src.type)) + modrm |= (base_register(dest) & 7) << 3; + else if (!is_displacement_only(src.type) && !is_immediate(src.type)) + modrm |= (base_register(src) & 7) << 3; // source register in reg + + if (is_scaled_addressing(dest.type)) modrm |= RSP; + else if (is_scaled_addressing(src.type) || is_absolute(src.type)) + modrm |= RSP; + else if (is_rip_relative(src.type)) + modrm |= RBP; + else modrm |= (base_register(dest) & 7); // destination register in r/m byte + + target->code().write(modrm); + if (has_sib) target->code().write(sib); + + if (is_displacement_only(src.type) || is_displacement_only(dest.type)) { + target->code().write(little_endian((i32)disp)); + } + else if (disp) { + if (disp > -129 && disp < 128) target->code().write((i8)disp); + else target->code().write(little_endian((i32)disp)); + } + + if (is_immediate(src.type)) write_immediate(src, src_size); + } + + void emitargs(const Arg& src, Size size, i8 imod = -1) { + Arg dummy; + dummy.type = REGISTER64; + dummy.data.reg = (Register)(u8)imod; + emitargs(src, dummy, size, imod); + } + + // utility function to help encode simple binary arithmetic ops + // - ADD OR ADC SBB AND SUB XOR CMP + void encode_arithmetic(const Arg& dest, const Arg& src, Size actual_size, i8 op) { + emitprefix(dest, src, actual_size); + if (is_immediate(src.type)) { // use opcode from 0x80 - 0x83 + Size src_size = operand_size(src.type); + if (src_size == AUTO) src_size = actual_size; + + Arg imm_src = src; + + i64 val = immediate_value(src); + if (val > -129 && val < 128) { + src_size = BYTE; + imm_src.type = IMM8; + } + + u8 opcode = 0x80; + if (actual_size != BYTE) opcode ++; // set bottom bit for non-8-bit mode + if (src_size == BYTE) opcode += 2; // set next lowest bit for 8-bit immediate + target->code().write(opcode); + + emitargs(dest, imm_src, actual_size, op); + } + else { // use normal opcode + u8 opcode = op * 8; + if (actual_size != BYTE) opcode ++; // set bottom bit for non-8-bit mode + if (is_memory(src.type)) opcode += 2; // set next lowest bit for memory source + target->code().write(opcode); + + if (is_memory(src.type)) emitargs(src, dest, actual_size); + else emitargs(dest, src, actual_size); + } + } + + void add(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + verify_args(dest, src); + Size actual_size = resolve_size(dest, src, size); + + encode_arithmetic(dest, src, actual_size, 0); + } + + void or_(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + verify_args(dest, src); + Size actual_size = resolve_size(dest, src, size); + + encode_arithmetic(dest, src, actual_size, 1); + } + + void adc(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + verify_args(dest, src); + Size actual_size = resolve_size(dest, src, size); + + encode_arithmetic(dest, src, actual_size, 2); + } + + void sbb(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + verify_args(dest, src); + Size actual_size = resolve_size(dest, src, size); + + encode_arithmetic(dest, src, actual_size, 3); + } + + void and_(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + verify_args(dest, src); + Size actual_size = resolve_size(dest, src, size); + + encode_arithmetic(dest, src, actual_size, 4); + } + + void sub(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + verify_args(dest, src); + Size actual_size = resolve_size(dest, src, size); + + encode_arithmetic(dest, src, actual_size, 5); + } + + void xor_(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + verify_args(dest, src); + Size actual_size = resolve_size(dest, src, size); + + encode_arithmetic(dest, src, actual_size, 6); + } + + void cmp(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + verify_args(dest, src); + Size actual_size = resolve_size(dest, src, size); + + encode_arithmetic(dest, src, actual_size, 7); + } + + void mov(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + verify_args(dest, src); + Size actual_size = resolve_size(dest, src, size); + + emitprefix(dest, src, actual_size); + if (is_immediate(src.type)) { + i64 value = immediate_value(src); + if (is_register(dest.type) && (value < -0x80000000l || value > 0x7fffffffl)) { + u8 opcode = 0xb0; + if (actual_size != BYTE) opcode += 8; // set bottom bit for non-8-bit mode + opcode += (dest.data.reg & 7); + target->code().write(opcode); + + write_immediate(src, actual_size, true); + } + else { + u8 opcode = 0xc6; + if (actual_size != BYTE) opcode ++; // set bottom bit for non-8-bit mode + target->code().write(opcode); + + emitargs(dest, src, actual_size); + } + } + else { // use normal opcode + u8 opcode = 0x88; + if (actual_size != BYTE) opcode ++; // set bottom bit for non-8-bit mode + if (is_memory(src.type)) opcode += 2; // set next lowest bit for memory source + target->code().write(opcode); + + if (is_memory(src.type)) emitargs(src, dest, actual_size); + else emitargs(dest, src, actual_size); + } + } + + void imul(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + verify_args(dest, src); + Size actual_size = resolve_size(dest, src, size); + + emitprefix(src, dest, actual_size); // operands are reversed...not sure why :p + if (is_immediate(src.type)) { + fprintf(stderr, "[ERROR] Invalid operand; immediate not permitted " + "in binary 'imul' instruction.\n"); + exit(1); + } + else if (is_memory(dest.type)) { + fprintf(stderr, "[ERROR] Invalid operand; destination in binary " + "'imul' instruction cannot be memory.\n"); + exit(1); + } + else { // use normal opcode + target->code().write(0x0f, 0xaf); + emitargs(src, dest, actual_size); // operands are reversed...not sure why :p + } + } + + void encode_shift(const Arg& dest, const Arg& shift, + Size dest_size, i8 op) { + emitprefix(dest, shift, dest_size); + if (is_immediate(shift.type)) { + Size src_size = operand_size(shift.type); + if (src_size == AUTO) src_size = dest_size; + + Arg imm_src = shift; + + i64 val = immediate_value(shift); + if (val > -129 && val < 128) { + src_size = BYTE; + imm_src.type = IMM8; + } + + u8 opcode = 0xc0; + if (src_size != BYTE) { + fprintf(stderr, "[ERROR] Cannot shift by more than -128 - 127, " + "given %ld.\n", val); + exit(1); + } + if (dest_size != BYTE) opcode += 1; // use 0xc1 for larger dests + if (val == 1) opcode += 0x10; // use 0xd0/d1 for shifts of 1 + target->code().write(opcode); + + emitargs(dest, dest_size, op); + if (val != 1) write_immediate(val, src_size); // immedate at end + } + else if (is_register(shift.type)) { // use normal opcode + if (base_register(shift) != RCX) { + fprintf(stderr, "[ERROR] Cannot shift by register other than CL.\n"); + exit(1); + } + u8 opcode = 0xd2; + if (dest_size != BYTE) opcode ++; // set bottom bit for non-8-bit mode + target->code().write(opcode); + emitargs(dest, dest_size, op); + } + } + + void rol(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + verify_args(dest, src); + Size dest_size = resolve_size(dest, size); + + encode_shift(dest, src, dest_size, 0); + } + + void ror(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + verify_args(dest, src); + Size dest_size = resolve_size(dest, size); + + encode_shift(dest, src, dest_size, 1); + } + + void rcl(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + verify_args(dest, src); + Size dest_size = resolve_size(dest, size); + + encode_shift(dest, src, dest_size, 2); + } + + void rcr(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + verify_args(dest, src); + Size dest_size = resolve_size(dest, size); + + encode_shift(dest, src, dest_size, 3); + } + + void shl(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + verify_args(dest, src); + Size dest_size = resolve_size(dest, size); + + encode_shift(dest, src, dest_size, 4); + } + + void shr(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + verify_args(dest, src); + Size dest_size = resolve_size(dest, size); + + encode_shift(dest, src, dest_size, 5); + } + + void sar(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + verify_args(dest, src); + Size dest_size = resolve_size(dest, size); + + encode_shift(dest, src, dest_size, 7); + } + + void idiv(const Arg& src, Size size) { + verify_buffer(); + Size actual_size = resolve_size(src, size); + + if (is_immediate(src.type)) { + fprintf(stderr, "[ERROR] Invalid operand; immediate not permitted " + "in 'idiv' instruction.\n"); + exit(1); + } + + emitprefix(src, actual_size); + target->code().write(actual_size == BYTE ? 0xf6 : 0xf7); + emitargs(src, actual_size, 7); + } + + void not_(const Arg& src, Size size) { + verify_buffer(); + Size actual_size = resolve_size(src, size); + + if (is_immediate(src.type)) { + fprintf(stderr, "[ERROR] Invalid operand; immediate not permitted " + "in 'not' instruction.\n"); + exit(1); + } + + emitprefix(src, actual_size); + target->code().write(actual_size == BYTE ? 0xf6 : 0xf7); + emitargs(src, actual_size, 2); + } + + void inc(const Arg& src, Size size) { + verify_buffer(); + Size actual_size = resolve_size(src, size); + + if (is_immediate(src.type)) { + fprintf(stderr, "[ERROR] Invalid operand; immediate not permitted " + "in 'inc' instruction.\n"); + exit(1); + } + + emitprefix(src, actual_size); + target->code().write(actual_size == BYTE ? 0xfe : 0xff); + emitargs(src, actual_size, 0); + } + + void dec(const Arg& src, Size size) { + verify_buffer(); + Size actual_size = resolve_size(src, size); + + if (is_immediate(src.type)) { + fprintf(stderr, "[ERROR] Invalid operand; immediate not permitted " + "in 'dec' instruction.\n"); + exit(1); + } + + emitprefix(src, actual_size); + target->code().write(actual_size == BYTE ? 0xfe : 0xff); + emitargs(src, actual_size, 1); + } + + void push(const Arg& src, Size size) { + verify_buffer(); + Size actual_size = resolve_size(src, size); + + emitprefix(src, actual_size); + if (is_immediate(src.type)) { + if (actual_size == BYTE) target->code().write(0x6a); + else target->code().write(0x68); + write_immediate(src, actual_size); + } + else if (is_memory(src.type)) { + target->code().write(0xff); + emitargs(src, actual_size, 6); + } + else { + target->code().write(0x50 + (src.data.reg & 7)); + } + } + + void pop(const Arg& src, Size size) { + verify_buffer(); + Size actual_size = resolve_size(src, size); + + emitprefix(src, actual_size); + if (is_immediate(src.type)) { + fprintf(stderr, "[ERROR] Invalid operand; immediate not permitted " + "in 'pop' instruction.\n"); + exit(1); + } + else if (is_memory(src.type)) { + target->code().write(0x8f); + emitargs(src, actual_size, 0); + } + else { + target->code().write(0x58 + (src.data.reg & 7)); + } + } + + void lea(const Arg& dest, const Arg& src, Size size) { + verify_buffer(); + Size actual_size = resolve_size(dest, src, size); + + emitprefix(dest, src, actual_size); + + if (is_immediate(src.type)) { + fprintf(stderr, "[ERROR] Invalid operand; immediate not permitted " + "in 'lea' instruction.\n"); + exit(1); + } + else if (is_register(src.type)) { + fprintf(stderr, "[ERROR] Invalid source operand; register not permitted " + "in 'lea' instruction.\n"); + exit(1); + } + else if (is_memory(src.type)) { + if (!is_register(dest.type)) { + fprintf(stderr, "[ERROR] Invalid dest operand; destination in 'lea' " + "instruction must be register.\n"); + } + Arg disp = src; + if (is_label(src.type)) disp = riprel64(0); + target->code().write(0x8d); + emitargs(dest, disp, actual_size); + if (is_label(src.type)) + target->reference(src.data.label, relative(DWORD), -4); + } + } + + void cdq() { + verify_buffer(); + + target->code().write(0x99); + } + + void ret() { + verify_buffer(); + + target->code().write(0xc3); + } + + void syscall() { + verify_buffer(); + + target->code().write(0x0f, 0x05); + } + + void label(Symbol symbol) { + target->define(symbol); + } + + void label(const char* symbol) { + label(local(symbol)); + } + + void jmp(const Arg& dest, Size size) { + verify_buffer(); + Size actual_size = resolve_size(dest, size); + + if (is_label(dest.type) || is_immediate(dest.type)) { + if (actual_size > DWORD) actual_size = DWORD; + i64 imm = 0; + if (is_immediate(dest.type)) imm = dest.data.imm64; + + if (imm < -0x8000000l || imm > 0x7fffffffl) { + fprintf(stderr, "[ERROR] Call offset too large; must fit within 32 bits.\n"); + exit(1); + } + + if (actual_size == WORD) target->code().write(0x66); + target->code().write(actual_size == BYTE ? 0xeb : 0xe9); // opcode + write_immediate(imm, actual_size); + + if (is_label(dest.type)) { + i8 imm_size = 1; + if (actual_size == WORD) imm_size = 2; + else if (actual_size == DWORD) imm_size = 4; + target->reference(dest.data.label, relative(actual_size), -imm_size); + } + } + else { + target->code().write(0xff); + emitargs(dest, actual_size, 4); + } + } + + void jcc(const Arg& dest, Condition condition) { + verify_buffer(); + Size size = operand_size(dest.type); + if (is_label(dest.type) || is_immediate(dest.type)) { + if (size == AUTO) { + fprintf(stderr, "[ERROR] Cannot deduce operand size in conditional jump instruction.\n"); + exit(1); + } + if (size > DWORD) size = DWORD; + + i64 imm = 0; + if (is_immediate(dest.type)) imm = dest.data.imm64; + + if (imm < -0x8000000l || imm > 0x7fffffffl) { + fprintf(stderr, "[ERROR] Call offset too large; must fit within 32 bits.\n"); + exit(1); + } + + if (size == WORD) target->code().write(0x66); + if (size == BYTE) target->code().write(0x70 + (u8)condition); // jcc rel8 + else target->code().write(0x0f, 0x80 + (u8)condition); // jcc rel16 / rel32 + + write_immediate(imm, size); + + if (is_label(dest.type)) { + i8 imm_size = 1; + if (size == WORD) imm_size = 2; + else if (size == DWORD) imm_size = 4; + target->reference(dest.data.label, relative(size), -imm_size); + } + } + else { + fprintf(stderr, "[ERROR] Cannot conditional jump by register or memory location.\n"); + exit(1); + } + } + + void call(const Arg& dest, Size size) { + verify_buffer(); + Size actual_size = resolve_size(dest, size); + if (actual_size <= BYTE) actual_size = WORD; // cannot call by smaller than dword + + if (is_label(dest.type) || is_immediate(dest.type)) { + if (actual_size > DWORD) actual_size = DWORD; + if (actual_size <= WORD) actual_size = DWORD; // cannot call by smaller than dword + + i64 imm = 0; + if (is_immediate(dest.type)) imm = dest.data.imm64; + + if (imm < -0x8000000l || imm > 0x7fffffffl) { + fprintf(stderr, "[ERROR] Call offset too large; must fit within 32 bits.\n"); + exit(1); + } + + target->code().write(0xe8); // opcode + write_immediate(imm, actual_size); + if (is_label(dest.type)) target->reference(dest.data.label, relative(actual_size), -4); + } + else { + target->code().write(0xff); + emitargs(dest, actual_size, 2); + } + } + + void setcc(const Arg& dest, Condition condition, Size size) { + verify_buffer(); + Size actual_size = resolve_size(dest, size); + + if (is_immediate(dest.type)) { + fprintf(stderr, "[ERROR] Invalid operand; immediate not permitted " + "in 'setcc' instruction.\n"); + exit(1); + } + else if (is_memory(dest.type)) { + fprintf(stderr, "[ERROR] Invalid operand; memory operand " + "not permitted in 'setcc' instruction.\n"); + exit(1); + } + else { + target->code().write(0x0f, 0x90 + (u8)condition); + emitargs(dest, actual_size, 0); + } + } +} \ No newline at end of file diff --git a/jasmine/x64.h b/jasmine/x64.h new file mode 100644 index 0000000..1668371 --- /dev/null +++ b/jasmine/x64.h @@ -0,0 +1,241 @@ +#ifndef JASMINE_X64_H +#define JASMINE_X64_H + +#include "utils.h" +#include "obj.h" + +namespace x64 { + enum Register : u8 { + RAX = 0, + RCX = 1, + RDX = 2, + RBX = 3, + RSP = 4, + RBP = 5, + RSI = 6, + RDI = 7, + R8 = 8, + R9 = 9, + R10 = 10, + R11 = 11, + R12 = 12, + R13 = 13, + R14 = 14, + R15 = 15, + INVALID = 255 + }; + + enum Scale : u8 { + SCALE1 = 0, + SCALE2 = 1, + SCALE4 = 2, + SCALE8 = 3 + }; + + enum Size : u8 { + BYTE = 0, + WORD = 1, + DWORD = 2, + QWORD = 3, + AUTO = 4 + }; + + enum Condition : u8 { + OVERFLOW = 0, + NOT_OVERFLOW = 1, + BELOW = 2, + NOT_ABOVE_OR_EQUAL = 2, + CARRY = 2, + NOT_BELOW = 3, + ABOVE_OR_EQUAL = 3, + NOT_CARRY = 3, + ZERO = 4, + EQUAL = 4, + NOT_ZERO = 5, + NOT_EQUAL = 5, + BELOW_OR_EQUAL = 6, + NOT_ABOVE = 6, + NOT_BELOW_OR_EQUAL = 7, + ABOVE = 7, + SIGN = 8, + NOT_SIGN = 9, + PARITY = 10, + PARITY_EVEN = 10, + NOT_PARITY = 11, + PARITY_ODD = 11, + LESS = 12, + NOT_GREATER_OR_EQUAL = 12, + NOT_LESS = 13, + GREATER_OR_EQUAL = 13, + LESS_OR_EQUAL = 14, + NOT_GREATER = 14, + NOT_LESS_OR_EQUAL = 15, + GREATER = 15 + }; + + struct ScaledRegister { + Register reg; + Scale scale; + + ScaledRegister(Register reg_in, Scale scale_in = SCALE1); + }; + + ScaledRegister operator*(Register reg, int scale); + + enum ArgType : u8 { + REGISTER8 = 0, + REGISTER16 = 1, + REGISTER32 = 2, + REGISTER64 = 3, + IMM8 = 4, + IMM16 = 5, + IMM32 = 6, + IMM64 = 7, + REGISTER_LABEL8 = 8, + REGISTER_LABEL16 = 9, + REGISTER_LABEL32 = 10, + REGISTER_LABEL64 = 11, + REGISTER_OFFSET8 = 12, + REGISTER_OFFSET16 = 13, + REGISTER_OFFSET32 = 14, + REGISTER_OFFSET64 = 15, + LABEL8 = 16, + LABEL16 = 17, + LABEL32 = 18, + LABEL64 = 19, + ABSOLUTE8 = 20, + ABSOLUTE16 = 21, + ABSOLUTE32 = 22, + ABSOLUTE64 = 23, + SCALED_INDEX8 = 24, + SCALED_INDEX16 = 25, + SCALED_INDEX32 = 26, + SCALED_INDEX64 = 27, + RIPRELATIVE8 = 28, + RIPRELATIVE16 = 29, + RIPRELATIVE32 = 30, + RIPRELATIVE64 = 31, + IMM_AUTO = 240, + REGISTER_LABEL_AUTO = 241, + REGISTER_OFFSET_AUTO = 242, + LABEL_AUTO = 243, + ABSOLUTE_AUTO = 244, + SCALED_INDEX_AUTO = 245, + RIPRELATIVE_AUTO = 246 + }; + + struct Arg { + union { + i8 imm8; + i16 imm16; + i32 imm32; + i64 imm64; + Register reg; + struct { + Register base; + jasmine::Symbol label; + } register_label; + struct { + Register base; + i64 offset; + } register_offset; + struct { + Register base, index; + Scale scale; + i64 offset; + } scaled_index; + jasmine::Symbol label; + i64 absolute; + i64 rip_relative; + } data; + + ArgType type; + }; + + bool is_register(ArgType type); + bool is_immediate(ArgType type); + bool is_memory(ArgType type); + bool is_label(ArgType type); + + void writeto(jasmine::Object& obj); + + Arg imm8(i8 value); + Arg imm16(i16 value); + Arg imm32(i32 value); + Arg imm64(i64 value); + Arg imm(i64 value); + + Arg r8(Register reg); + Arg r16(Register reg); + Arg r32(Register reg); + Arg r64(Register reg); + + Arg m8(Register reg, i64 offset); + Arg m16(Register reg, i64 offset); + Arg m32(Register reg, i64 offset); + Arg m64(Register reg, i64 offset); + Arg mem(Register reg, i64 offset); + Arg m8(Register base, Register index, Scale scale, i64 offset); + Arg m16(Register base, Register index, Scale scale, i64 offset); + Arg m32(Register base, Register index, Scale scale, i64 offset); + Arg m64(Register base, Register index, Scale scale, i64 offset); + Arg mem(Register base, Register index, Scale scale, i64 offset); + Arg m8(Register base, ScaledRegister index, i64 offset); + Arg m16(Register base, ScaledRegister index, i64 offset); + Arg m32(Register base, ScaledRegister index, i64 offset); + Arg m64(Register base, ScaledRegister index, i64 offset); + Arg mem(Register base, ScaledRegister index, i64 offset); + + Arg abs8(i64 offset); + Arg abs16(i64 offset); + Arg abs32(i64 offset); + Arg abs64(i64 offset); + Arg abs(i64 offset); + + Arg riprel8(i64 offset); + Arg riprel16(i64 offset); + Arg riprel32(i64 offset); + Arg riprel64(i64 offset); + Arg riprel(i64 offset); + + Arg label8(jasmine::Symbol symbol); + Arg label16(jasmine::Symbol symbol); + Arg label32(jasmine::Symbol symbol); + Arg label64(jasmine::Symbol symbol); + + void add(const Arg& dest, const Arg& src, Size size = AUTO); + void or_(const Arg& dest, const Arg& src, Size size = AUTO); + void adc(const Arg& dest, const Arg& src, Size size = AUTO); + void sbb(const Arg& dest, const Arg& src, Size size = AUTO); + void and_(const Arg& dest, const Arg& src, Size size = AUTO); + void sub(const Arg& dest, const Arg& src, Size size = AUTO); + void xor_(const Arg& dest, const Arg& src, Size size = AUTO); + void cmp(const Arg& dest, const Arg& src, Size size = AUTO); + void mov(const Arg& dest, const Arg& src, Size size = AUTO); + void imul(const Arg& dest, const Arg& src, Size size = AUTO); + void rol(const Arg& dest, const Arg& src, Size size = AUTO); + void ror(const Arg& dest, const Arg& src, Size size = AUTO); + void rcl(const Arg& dest, const Arg& src, Size size = AUTO); + void rcr(const Arg& dest, const Arg& src, Size size = AUTO); + void shl(const Arg& dest, const Arg& src, Size size = AUTO); + void shr(const Arg& dest, const Arg& src, Size size = AUTO); + void sar(const Arg& dest, const Arg& src, Size size = AUTO); + void idiv(const Arg& src, Size size = AUTO); + void not_(const Arg& src, Size size = AUTO); + void inc(const Arg& src, Size size = AUTO); + void dec(const Arg& src, Size size = AUTO); + void push(const Arg& src, Size size = AUTO); + void pop(const Arg& src, Size size = AUTO); + void lea(const Arg& dest, const Arg& src, Size size = AUTO); + void cdq(); + void ret(); + void syscall(); + void label(jasmine::Symbol symbol); + void label(const char* name); + void jmp(const Arg& dest, Size size = AUTO); + void jcc(const Arg& dest, Condition condition); + void call(const Arg& dest, Size size = AUTO); + void setcc(const Arg& dest, Condition condition, Size size = AUTO); +} + +#endif \ No newline at end of file diff --git a/lex.cpp b/lex.cpp index 7a35856..2a844d0 100644 --- a/lex.cpp +++ b/lex.cpp @@ -1,5 +1,5 @@ #include "lex.h" -#include "io.h" +#include "util/io.h" #include "errors.h" #include #include @@ -13,7 +13,7 @@ namespace basil { } static const char* TOKEN_NAMES[NUM_TOKEN_TYPES] = { - "none", "int", "symbol", "coeff", "left paren", "right paren", + "none", "int", "symbol", "string", "coeff", "left paren", "right paren", "left bracket", "right bracket", "left brace", "right brace", "semicolon", "dot", "colon", "pipe", "plus", "minus", "quote", "newline" @@ -26,7 +26,7 @@ namespace basil { T_NONE, T_NONE, T_NEWLINE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 08 T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 10 T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 18 - T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 20 + T_NONE, T_NONE, T_STRING, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 20 T_LPAREN, T_RPAREN, T_NONE, T_NONE, T_NONE, T_NONE, T_DOT, T_NONE, // 28 T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 30 T_NONE, T_NONE, T_COLON, T_SEMI, T_NONE, T_NONE, T_NONE, T_NONE, // 38 @@ -71,6 +71,20 @@ namespace basil { while (view.peek() && view.peek() != '\n') view.read(); return scan(view); } + else if (ch == '"') { + view.read(); + while (view.peek() && view.peek() != '\n' && view.peek() != '"') { + if (view.read() == '\\' && view.peek() == '"') view.read(); + } + if (view.peek() != '"') + err({ line, u16(view.col()) }, + "Expected closing quote in string literal."); + else { + view.read(); + return Token(T_STRING, { u32(view.pos() - start), start }, + line, start_col); + } + } else if (ch == '.') { while (view.peek() == '.') view.read(); u32 len = view.pos() - start; @@ -113,7 +127,7 @@ namespace basil { view.read(); return scan(view); } - else err({ line, u16(view.col()) }, "Unexpected character in input '", view.peek(), "'."); + else err({ line, u16(view.col()) }, "Unexpected character in input '", view.read(), "'."); return NONE; } diff --git a/lex.h b/lex.h index 442223b..ec33b1a 100644 --- a/lex.h +++ b/lex.h @@ -1,13 +1,13 @@ #ifndef BASIL_LEX_H #define BASIL_LEX_H -#include "defs.h" +#include "util/defs.h" #include "source.h" namespace basil { enum TokenType : u8 { T_NONE, - T_INT, T_SYMBOL, T_COEFF, + T_INT, T_SYMBOL, T_STRING, T_COEFF, T_LPAREN, T_RPAREN, T_LBRACK, T_RBRACK, T_LBRACE, T_RBRACE, T_SEMI, T_DOT, T_COLON, T_PIPE, T_PLUS, T_MINUS, T_QUOTE, diff --git a/main b/main deleted file mode 100644 index f970ffe3a7e0a99a136c29fafc20420b70c4d773..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 245824 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 env, const Value& args) { + repl_done = true; + return Value(VOID); +} + +Value print_tokens(ref env, const Value& args) { + if (!args.get_product()[0].is_bool()) { + err(args.get_product()[0].loc(), "Print tokens command requires bool, '", + args.get_product()[0], "' provided."); + return ERROR; + } + basil::print_tokens(args.get_product()[0].get_bool()); + return Value(VOID); +} + +Value print_parse(ref env, const Value& args) { + if (!args.get_product()[0].is_bool()) { + err(args.get_product()[0].loc(), "Print parse tree command requires bool, '", + args.get_product()[0], "' provided."); + return ERROR; + } + basil::print_parsed(args.get_product()[0].get_bool()); + return Value(VOID); +} + +Value print_ast(ref env, const Value& args) { + if (!args.get_product()[0].is_bool()) { + err(args.get_product()[0].loc(), "Print AST command requires bool, '", + args.get_product()[0], "' provided."); + return ERROR; + } + basil::print_ast(args.get_product()[0].get_bool()); + return Value(VOID); +} + +Value print_ssa(ref env, const Value& args) { + if (!args.get_product()[0].is_bool()) { + err(args.get_product()[0].loc(), "Print SSA command requires bool, '", + args.get_product()[0], "' provided."); + return ERROR; + } + basil::print_ssa(args.get_product()[0].get_bool()); + return Value(VOID); +} + +Value print_asm(ref env, const Value& args) { + if (!args.get_product()[0].is_bool()) { + err(args.get_product()[0].loc(), "Print ASM command requires bool, '", + args.get_product()[0], "' provided."); + return ERROR; + } + basil::print_asm(args.get_product()[0].get_bool()); + return Value(VOID); +} + +Value repl_help(ref env, const Value& args) { + println(""); + println("Commands: "); + println(" - $help => prints this command list."); + println(" - $quit => exits REPL."); + println(" - $print-tokens => toggles token printing."); + println(" - $print-parse => toggles parse tree printing."); + println(" - $print-ast => toggles AST printing."); + println(" - $print-ssa => toggles SSA printing."); + println(" - $print-asm => toggles assembly printing."); + println(""); + return Value(VOID); +} + +int intro(); + +int main(int argc, char** argv) { + if (argc == 1) { // repl mode + print_banner(); + println(BOLDGREEN, "Enter any Basil expression at the prompt, or '", + RESET, "$help", BOLDGREEN, "' for a list of commands!", RESET); + println(""); + Source src; + + ref root = create_root_env(); + root->def("$help", new FunctionValue(root, repl_help, 0), 0); + root->def("$quit", new FunctionValue(root, repl_quit, 0), 0); + root->def("$print-tokens", new FunctionValue(root, print_tokens, 1), 1); + root->def("$print-parse", new FunctionValue(root, print_parse, 1), 1); + root->def("$print-ast", new FunctionValue(root, print_ast, 1), 1); + root->def("$print-ssa", new FunctionValue(root, print_ssa, 1), 1); + root->def("$print-asm", new FunctionValue(root, print_asm, 1), 1); + Env global_env(root); + ref global(global_env); + Function main_fn("main"); + + while (!repl_done) repl(global, src, main_fn); + return 0; + } + else if (string(argv[1]) == "intro") { + return intro(); + } + else if (argc == 3 && string(argv[1]) == "run") { + Source src(argv[2]); + return run(src); + } + else if (argc > 2 && string(argv[1]) == "exec") { + Source src; + string code; + for (int i = 2; i < argc; i ++) { + code += argv[i]; + code += ' '; + } + src.add_line(code); + return run(src); + } + else if (string(argv[1]) != "help") { + Source src(argv[1]); + return run(src); + } + + print_banner(); + + println(BOLDGREEN, "Welcome to the Basil programming language!", RESET); + println(""); + println("Commands: "); + println(" - basil => starts REPL."); + println(" - basil => executes ."); + println(" - basil help => prints usage information."); + println(" - basil intro => runs interactive introduction."); + println(" - basil exec => executes ."); + println(""); +} + +static bool running_intro = false; +static i64 intro_section = 0; +static i64 intro_section_index = 0; + +Value intro_start(ref env, const Value& args) { + running_intro = true; + return Value(VOID); +} + +Value intro_help(ref env, const Value& args) { + println(""); + println("Commands: "); + println(" - $help => prints this command list."); + println(" - $quit => exits tour."); + println(" - $start => starts tour."); + println(" - $contents => prints table of contents."); + println(" - $section => skips to section with number ."); + println(""); + return Value(VOID); +} + +Value intro_contents(ref env, const Value& args) { + println(""); + println("Sections: "); + println(BOLDWHITE, "1. Basics", RESET); + println(" - Hello, World!"); + println(" - Constants"); + println(" - Lists"); + println(" - Expressions"); + println(" - Indentation"); + println(" - Quotation"); + println(BOLDWHITE, "2. Definitions", RESET); + println(" - Variables"); + println(" - Procedures"); + println(" - Lambdas"); + println(BOLDWHITE, "3. Arithmetic and Logic", RESET); + println(" - Arithmetic Operators"); + println(" - Booleans"); + println(" - Logical Operators"); + println(" - Comparisons"); + println(BOLDWHITE, "4. Infix Operators", RESET); + println(" - Binary Operators"); + println(" - Unary Operators"); + println(" - Higher-Arity Operators"); + println(" - Mixfix Procedures"); + println(BOLDWHITE, "5. Control Flow", RESET); + println(" - If Expressions"); + println(" - While Loops"); + println(BOLDWHITE, "6. Metaprogramming", RESET); + println(" - Splices"); + println(" - Aliases"); + println(" - Macro Procedures"); + println(" - Macro Operators"); + println(BOLDWHITE, "7. Imports", RESET); + println(" - Use Statement"); + println(""); + return Value(VOID); +} + +Value intro_set_section(ref env, const Value& args) { + if (!args.get_product()[0].is_int()) { + err(args.get_product()[0].loc(), "Expected integer for section number, given ", + args.get_product()[0]); + return Value(ERROR); + } + intro_section = args.get_product()[0].get_int() - 1; + intro_section_index = 0; + return intro_start(env, args); +} + +struct IntroStep { + const char* text; + const char* instruction; + Value expected; + const char* title = nullptr; +}; + +struct IntroSection { + vector steps; +}; + +static vector intro_sections; + +void add_step(IntroStep step) { + intro_sections.back().steps.push(step); +} + +Value runtime(const Type* t) { + return new ASTSingleton(t); +} + +void init_intro_sections() { + intro_sections.push({}); // Basics + add_step({ +R"(Welcome to Basil! +Basil is a lightweight programming language, which aims +to make it as easy as possible to write expressive and +performant code. We'll talk about some of the details +later, but for now, let's write a hello-world program! +)", +R"(Type 'display "hello world"' into the prompt, then +press Enter twice. +)", runtime(VOID), "Basics"}); + add_step({ +R"(All Basil programs are made up of terms. The simplest +terms are constants, which evaluate to themselves. For +example, an integer constant. +)", +R"(Type '12' into the prompt, then press Enter twice. +)", Value(12)}); + add_step({ +R"(In addition to integer constants, which represent +numbers, Basil supports string constants, which represent +pieces of text. +)", +R"(Type '"hello"' into the prompt, then press Enter twice. +)", Value("hello", STRING)}); + add_step({ +R"(Basil also supports values that contain multiple terms. +These values are called lists. If you're familiar with +any Lisp dialects, these will probably be pretty familiar +to you. To express a list value, we enclose other +terms in square brackets. +)", +R"(Type '[1 2 3]' into the prompt, then press Enter twice. +)", list_of(1, 2, 3)}); + add_step({ +R"(The empty list is a little special in Basil, but it can +be written about as you'd expect. It generally represents +the absence of a value. +)", +R"(Type '[]' into the prompt, then press Enter twice. +)", empty()}); + add_step({ +R"(Lists can be useful for storing data, but they can also +be used to represent code. Basil programs themselves are +just lists of terms that the Basil compiler evaluates. +To write a list we want to be evaluated, we enclose other +terms in parentheses. +)", +R"(Type '(+ 1 2)' into the prompt, then press Enter twice. +)", Value(3)}); + add_step({ +R"(This is an example of evaluation. '+' is a symbol that +represents a built-in function to add two numbers. When +we evaluate a list that starts with a function, the result +of evaluation is the result of that function. So, (+ 1 2) +is 3. + +Writing all these parentheses can be kind of a pain! So +Basil treats lines on the top level of the program as +lists, as if they were surrounded by parentheses. +)", +R"(Type '* 3 3' into the prompt, then press Enter twice. +)", Value(9)}); + add_step({ +R"(Basil programs can contain multiple terms. This is +why you have to press Enter twice at the REPL - to be +able to enter multiple terms at once. The program +evaluates every term, then returns the last result. +)", +R"(Type: + 1 + 2 + 3 +into the prompt, pressing Enter after each one. Press Enter +again after the last line. +)", Value(3)}); + add_step({ +R"(We can write expressions that evaluate others this way +using the 'do' special form. 'do' will evaluate each expression +in its body, then return the last result - in a way that can be +used in other expressions, like '*'. +)", +R"(Type '* 2 (do 1 2 3)' into the prompt, then press Enter twice. +)", Value(6)}); + add_step({ +R"(Sometimes, we want to 'do' quite a lot of things. In +these cases, it can be helpful to split a line into +multiple using an indented block. The indentation level +doesn't matter, as long as it's consistently more than +the starting line. +)", +R"(Type: + do + 1 + 2 + 3 +into the prompt, then press Enter twice. +)", Value(3)}); + add_step({ +R"(So, we evaluate terms to get values, and those values +are the result of our program. But what if we want to get +the value of some bit of code before it's evaluated? For +this, we use the 'quote' special form. +)", +R"(Type 'quote x' into the prompt, then press Enter twice. +)", Value("x")}); + add_step({ +R"(We 'quote' x before it's evaluated, and get a symbol +value. Symbols represent unique names, not bits of text +like strings. +The 'quote' special form can be abbreviated with a ':'. +)", +R"(Type ':x' into the prompt, then press Enter twice. +)", Value("x")}); + add_step({ +R"(That's the basics! But there's a lot more to Basil that +we'll cover in the next sections. +)", +R"(Enter ':okay!' into the prompt when you're ready to continue. +)", Value("okay!")}); + intro_sections.push({}); // Definitions + add_step({ +R"(We've discussed how to write simple expressions and +constants in Basil up until this point. But for more +complex programs, we might want to assign certain values +names. We can do just that with the 'def' special form. +)", +R"(Enter: + def x 1 + x +into the prompt. +)", Value(1), "Definitions"}); + add_step({ +R"('def' defines a variable in an environment. When a +symbol is evaluated in an environment, it evaluates to +the value of the associated variable, if it exists. This +value can be used in arbitrary expressions. +)", +R"(Enter: + def x 1 + def y 2 + + x y +into the prompt. +)", Value(3)}); + add_step({ +R"(We can also define procedures. Procedures take arguments, +and produce a resulting value. We've seen a few built-in +procedures so far, like '+' and '*'. Now, we can define our +own procedures! +)", +R"(Enter: + def (square x) + * x x + square 12 +into the prompt. +)", Value(144)}); + add_step({ +R"(We can add more arguments to a procedure by adding them +to the list after the 'def'. +)", +R"(Enter: + def (add-squares x y) + + (* x x) (* y y) + add-squares 3 4 +into the prompt. +)", Value(25)}); + add_step({ +R"(If a procedure has multiple terms in its body, it will +return the last result, as if they were wrapped in a 'do'. +)", +R"(Enter: + def (print x y) + display x + display y + print 1 2 +into the prompt. +)", runtime(VOID)}); + add_step({ +R"(If a procedure has no arguments, it will evaluate without +arguments. +)", +R"(Enter: + def (greet) + display "hello world" + greet +into the prompt. +)", runtime(VOID)}); + add_step({ +R"(Sometimes, we might want to produce a function value +without giving it a name. In these cases, we can use the +'lambda' special form to define an anonymous function. +)", +R"(Enter '(lambda (x y) (+ x y)) 1 2' into the prompt. +)", Value(3)}); + add_step({ +R"(That's how definitions work! Next, we'll go over the +built-in operations Basil supports, before getting into +control flow in the section after. +)", +R"(Enter ':okay!' into the prompt when you're ready to continue. +)", Value("okay!")}); + intro_sections.push({}); // Arithmetic and such + add_step({ +R"(So far we've introduced the '+' and '*' operators, +which add and multiply numbers. Basil also supports '-', '/', +and '%', each of which take two numbers and find the +difference, quotient, or remainder respectively. +)", +R"(Enter any expression that evaluates to '81' into the prompt. +)", Value(81), "Arithmetic and Logic"}); + add_step({ +R"(Basil includes syntactic sugar for prefix '-' and +'+'. If either of these are written before a term (with +no space), they act as unary minus and plus operators. +)", +R"(Enter any expression that evaluates to '-16' into the prompt. +)", Value(-16)}); + add_step({ +R"(Basil also includes syntactic sugar for coefficients. +If a number is written immediately before another term +(with no space), it will multiply the term by the number. +)", +R"(Enter: + def x 4 + 3x + 1 +into the prompt. +)", Value(13)}); + add_step({ +R"(In addition to numbers, Basil also has support for a +boolean type, which can be either of the values 'true' or +'false'. +)", +R"(Enter 'true' into the prompt. +)", Value(true, BOOL)}); + add_step({ +R"(The 'and', 'or', and 'xor' procedures can be used to +compute the logical and, inclusive-or, or exclusive-or of +two booleans respectively.. +)", +R"(Enter 'or (and true false) (xor true true)' into the prompt. +)", Value(false, BOOL)}); + add_step({ +R"(The 'not' procedure can be used to compute the logical +complement of a boolean value. +)", +R"(Enter 'not false' into the prompt. +)", Value(true, BOOL)}); + add_step({ +R"(We can get boolean values by comparing integers or +strings. The '==' and '!=' procedures return whether two +inputs are equal or inequal, respectively. +)", +R"(Enter '== "cat" "cat"' into the prompt. +)", Value(true, BOOL)}); + add_step({ +R"(We can also compare values by some order. The '<' +and '>' procedures return whether the first argument +is less than or greater than the second, respectively. +'<=' and '>=' behave the same as '<' and '>', but also +return true if the arguments are equal. +)", +R"(Enter '< 12 7' into the prompt. +)", Value(false, BOOL)}); + add_step({ +R"(That's all the boolean and arithmetic operators +Basil supports! Next up, we'll get into infix +operators, a more unique Basil feature to help write +these expressions more naturally. +)", +R"(Enter ':okay!' into the prompt when you're ready to continue. +)", Value("okay!")}); + intro_sections.push({}); // Infix operators + add_step({ +R"(Until this point, all expressions have been written +in 'prefix notation', with the procedure coming first, +followed by some arguments. This is great for parsers, +but it can be kind of unfamiliar, and inconvenient! +Basil supports this notation, but also allows some +procedures to be used 'infix', placed in-between their +arguments. +)", +R"(Enter '1 + 2' into the prompt. +)", Value(3), "Infix Operators"}); + add_step({ +R"(All of the arithmetic and logical operators we've +seen so far can be used infix. To determine which +operator applies to what, each operator has a certain +precedence. Operators of higher precedence, like '*', +bind more tightly than '+'. +)", +R"(Enter '1 + 2 * 3' into the prompt. +)", Value(7)}); + add_step({ +R"(Chains of infix operators are actually translated +by the compiler into equivalent prefix notation before +they are evaluated. This happens in standalone lists, +like in the previous two examples, and also in the +argument lists of procedure calls or special forms. +)", +R"(Enter: + def (double x) + x * 2 + double 10 / 5 +into the prompt. +)", Value(4)}); + add_step({ +R"(Infix operators can be defined by the user as well. +This is done with the 'infix' special form. 'infix' +operates very similarly to 'def', but with two key +differences: + - 'infix' accepts an integer constant after the 'infix' + symbol, to be used as the operator's precedence. + - The name of the procedure is the second element of + the argument list, not the first. +)", +R"(Enter: + infix (x add y) + x + y + 1 add 2 add 3 +into the prompt. +)", Value(6)}); + add_step({ +R"(Infix operators don't have to be binary operators. +A unary infix operator behaves like a postfix function. +)", +R"(Enter: + infix (x squared) + x * x + 13 squared +into the prompt. +)", Value(169)}); + add_step({ +R"(Infix operators can have more than two arguments too. +The operator is still written after the first argument, +but more than one argument may follow it. +)", +R"(Enter: + infix (s scale x y z) + [x * s y * s z * s] + 2 scale 4 5 6 +into the prompt. +)", list_of(8, 10, 12)}); + add_step({ +R"(Basil also supports a lesser-known concept known as +mixfix syntax. Every Basil procedure definition (so, +both 'def' and 'infix') allows one or more keywords in +the argument list. These keywords aren't arguments, but +must be present in the function's invocation. This +can help make functions with many arguments more readable. +)", +R"(Enter: + def (add x :to y) + x + y + add 1 to 2 +into the prompt. +)", Value(3)}); + add_step({ +R"(Mixfix syntax can allow a single function invocation +to span multiple lines. This only occurs when the +following lines start with keywords of said function. +In order for this to work, the keywords also need to be +explicitly quoted. +)", +R"(Enter: + def (first x :then y :finally z) + x + y + z + first + "hello" + :then + "hi there" + :finally + "goodbye" +into the prompt. +)", Value("goodbye", STRING)}); + add_step({ +R"(That's the overview of infix and mixfix procedures! +We think these are some of the coolest features Basil +has to offer. A lot of common programming patterns can +be expressed in very natural terms, without sacrificing +homoiconicity or performance. Next, we'll be going over +Basil's control-flow primitives. +)", +R"(Enter ':okay!' into the prompt when you're ready to continue. +)", Value("okay!")}); + intro_sections.push({}); // Control flow + add_step({ +R"(The simplest conditional expression Basil supports +is the '?' operator. '?' is an infix, ternary operator, +that takes a condition and two pieces of code. If the +condition is true, it evaluates and returns the first; +otherwise, it evaluates and returns the second. +)", +R"(Enter '4 < 5 ? "less" "greater"' into the prompt. +)", Value("less", STRING), "Control Flow"}); + add_step({ +R"(For more complex conditionals, we found that it was +nice to have a little more structure. So the 'if' +special form exists as an alternative to '?'. 'if', +like '?', picks between two pieces of code based on a +condition. For 'if', though, the case if false needs to +be preceded by the keyword 'else'. +)", +R"(Enter 'if true :yes else :no"' into the prompt. +)", Value("yes")}); + add_step({ +R"('if' can have multiple conditions in the same list, +using any number of 'elif' keywords. Each 'elif' +behaves like its own 'if', within the larger expression. +)", +R"(Enter: + def x 2 + if x == 1 "one" + :elif x == 2 "two" + :else "many" +into the prompt. +)", Value("two", STRING)}); + add_step({ +R"(Unlike '?', 'if' can have multiple terms in the +body of each conditional. If multiple terms are +provided, each is evaluated and the last result is +returned when the corresponding condition is true. +)", +R"(Enter: + if true + display "it's true!" + display "hooray!" + :else + display "it's false..." +into the prompt. +)", runtime(VOID)}); + add_step({ +R"(Basil also has a 'while' special form. This +operates similarly to if, but instead of evaluating +its body once, it will evaluate its body until its +condition is false. +)", +R"(Enter: + def x 0 + while x < 10 + display x + x = x + 1 +into the prompt. +)", runtime(VOID)}); + add_step({ +R"(That's all the built-in control flow Basil has! +We intentionally kept it short, since other Basil +features allow more specific control flow to be +user-defined - including the next topic, macros! +)", +R"(Enter ':okay!' into the prompt when you're ready to continue. +)", Value("okay!")}); + intro_sections.push({}); // Macros + add_step({ +R"(One of the biggest advantages of a simple, homoiconic +syntax is being able to support macros. In Basil, macros +behave very similarly to variables and procedures, only +operating on source code instead of values. The building +block of all of this is the splice special form, which +will evaluate a list of terms and insert the result in +its place in the source code. +)", +R"(Enter 'quote (1 + (splice 1 + 1))' into the prompt. +)", list_of(1, Value("+"), 2), "Macros"}); + add_step({ +R"(As a bit of syntactic sugar, any terms enclosed in '|' +characters become a splice. +)", +R"(Enter 'quote (|2 * 2|)' into the prompt. +)", list_of(4)}); + add_step({ +R"(The simplest kind of macro is an alias, kind of like +a macro variable. It'll replace itself wherever it's used +with a Basil term. For example, let's say you prefer 'let' +instead of 'def' to define variables: +)", +R"(Enter: + macro let def + let x 1 + x +into the prompt. +)", Value(1)}); + add_step({ +R"(We can also define macro procedures. These work just +like normal procedures, only they're defined with 'macro' +instead of 'def', and they automatically quote their +arguments and body. Instead of evaluating the body to get +a value, macro procedures splice code into the body to +generate the desired code. +)", +R"(Enter: + macro (dotwice expr) + |expr| + |expr| + dotwice (display "hello") +into the prompt. +)", runtime(VOID)}); + add_step({ +R"(Like normal procedures, macros can also be +declared infix. The 'infix-macro' special form is used +which, like 'infix', can optionally be given a precedence. +)", +R"(Enter: + infix-macro (expr unless cond) + if |cond| [] else |expr| + (display "bad thing!") unless 1 == 1 +into the prompt. +)", Value(VOID)}); + add_step({ +R"(Again like normal procedures, macros can be declared +mixfix, with any number of keywords. This applies to both +'macro' and 'infix-macro'. +)", +R"(Enter: + macro (print expr :if cond) + if |cond| + display |expr| + :else [] + print "hello" if (3 > 2) +into the prompt. +)", runtime(VOID)}); + add_step({ +R"(That's actually all there is to macros! They're not +too special or complex compared to other functions, +despite giving you direct control over the language's +syntax. Like infix functions, this is one of the Basil +features we're really proud of! +)", +R"(Enter ':okay!' into the prompt when you're ready to continue. +)", Value("okay!")}); + intro_sections.push({}); // Macros + add_step({ +R"(The last main feature of Basil is importing other +source files. This is done very simply, using the 'use' +special form. 'use' takes one argument, a symbol, and +loads the file at that path (with a '.bl' file extension +appended to it). +)", +R"(Enter: + use std/list + sort [4 2 3 1] +into the prompt. +)", runtime(find(INT)), "Imports"}); + add_step({ +R"('use' will evaluate the file it's provided, but instead +of producing any side-effects or values, it will simply +take the definitions produced from the file and add them +to the current environment. +There's really not too much else to say about it, +but you can take a look around the project directory +and play around with some of the functions we've written! +Or write your own files if you like!. +)", +R"(Enter ':okay!' into the prompt when you're ready to continue. +)", Value("okay!")}); +} + +#include +#include + +void print_intro_conclusion() { + println(BOLDGREEN, "Congratulations! \n", RESET); + println( +R"(Congratulations! You've completed the Basil +interactive tour! + +If you'd like to continue to play around with the +language, try running './basil help' in command line +for a full list of Basil commands! + +If you really liked the project, consider: + - starring it on GitHub! https://github.com/basilTeam/basil + - following the devs on Twitter! (@elucentdev, @minchyAK) + +If you want to get in touch, a Twitter DM (handles above) +or a Discord message (elucent#1839, minchy#2474) would be +great. + +Bye for now! +)"); +} + +int intro() { + srand(time(0)); + init_intro_sections(); + + print_banner(); + println(BOLDGREEN, "Welcome to the interactive Basil tour!", RESET); + println(BOLDGREEN, "Press Enter twice after entering a command to submit it.", RESET); + + Source src; + + ref root = create_root_env(); + root->def("$quit", new FunctionValue(root, repl_quit, 0), 0); + root->def("$help", new FunctionValue(root, intro_help, 0), 0); + root->def("$contents", new FunctionValue(root, intro_contents, 0), 0); + root->def("$start", new FunctionValue(root, intro_start, 0), 0); + root->def("$section", new FunctionValue(root, intro_set_section, 1), 1); + + intro_help(root, Value(VOID)); + + const char* congratulations[] = { + "Awesome!", + "Great!", + "Nice!", + "Cool!", + "You did it!", + "You got it!" + }; + + while (!repl_done) { + Env global_env(root); + ref global(global_env); + Function main_fn("main"); + + bool is_running_intro = running_intro; + if (is_running_intro) { + const IntroStep& step = intro_sections[intro_section].steps[intro_section_index]; + if (step.title) + println(intro_section + 1, ". ", BOLDGREEN, step.title, RESET, '\n'); + println(BOLDWHITE, step.text, RESET); + println(ITALICWHITE, step.instruction, RESET); + } + + if (is_running_intro) { + bool correct = false; + + while (!correct && !repl_done) { + Value result = repl(global, src, main_fn); + + const IntroStep& step = intro_sections[intro_section].steps[intro_section_index]; + if (result.is_runtime() && step.expected.is_runtime()) + correct = result.type() == step.expected.type(); + else correct = result == step.expected; + + if (!correct) + println(ITALICRED, "Expected '", step.expected, "', but given '", + result, "'.", RESET, '\n'); + else { + println(ITALICWHITE, congratulations[rand() % 6], RESET); + intro_section_index ++; + if (intro_section_index >= intro_sections[intro_section].steps.size()) + intro_section ++, intro_section_index = 0; + println(""); + usleep(1000000); + println("⸻", "\n"); + if (intro_section >= intro_sections.size()) + repl_done = true, print_intro_conclusion(); + } + } + } + else repl(global, src, main_fn); + } -int main() { - Source repl; - - ref root = create_root_env(); - ref global(root); - - while (true) { - print("? "); - Source::View view = repl.expand(_stdin); - - vector tokens; - while (view.peek()) - tokens.push(scan(view)); - if (error_count()) { - print_errors(_stdout, repl), clear_errors(); - clear_errors(); - continue; - } - else if (PRINT_TOKENS) { - print(BOLDYELLOW, "⬤ "); - for (const Token& t : tokens) print(t, " "); - println(RESET); - } - - vector lines; - TokenView tview(tokens, repl, true); - while (tview.peek()) { - Value line = parse_line(tview, tview.peek().column); - if (!line.is_void()) lines.push(line); - } - if (error_count()) { - print_errors(_stdout, repl), clear_errors(); - clear_errors(); - continue; - } - else if (PRINT_AST) for (Value v : lines) - println(BOLDGREEN, "∧ ", v, RESET); - - vector results; - for (Value v : lines) results.push(eval(global, v)); - - if (error_count()) { - print_errors(_stdout, repl), clear_errors(); - clear_errors(); - continue; - } - else if (PRINT_EVAL) - println(BOLDBLUE, "= ", results.back(), RESET, "\n"); - } + return 0; } \ No newline at end of file diff --git a/native.cpp b/native.cpp new file mode 100644 index 0000000..26df509 --- /dev/null +++ b/native.cpp @@ -0,0 +1,118 @@ +#include "native.h" +#include "values.h" +#include "util/io.h" +#include + +namespace basil { + using namespace jasmine; + + void add_native_function(Object& object, const string& name, void* function) { + using namespace x64; + writeto(object); + Symbol sym = global((const char*)name.raw()); + label(sym); + mov(r64(RAX), imm(i64(function))); + call(r64(RAX)); + ret(); + } + + void* _cons(i64 value, void* next) { + void* result = malloc(sizeof(i64) + sizeof(void*)); + *(i64*)result = value; + *((void**)result + 1) = next; + return result; + } + + void _display_int(i64 value) { + println(value); + } + + void _display_symbol(u64 value) { + println(symbol_for(value)); + } + + void _display_bool(bool value) { + println(value); + } + + void _display_string(const char* value) { + println(value); + } + + template + void _display_list(void* value) { + print("("); + bool first = true; + while (value) { + T i = (T)*(u64*)value; + print(first ? "" : " ", i); + value = *((void**)value + 1); + first = false; + } + println(")"); + } + + void _display_symbol_list(void* value) { + print("("); + bool first = true; + while (value) { + u64 i = *(u64*)value; + print(first ? "" : " ", symbol_for(i)); + value = *((void**)value + 1); + first = false; + } + println(")"); + } + + void _display_native_string_list(void* value) { + print("("); + bool first = true; + while (value) { + const char* i = *(const char**)value; + print(first ? "\"" : " \"", i, '"'); + value = *((void**)value + 1); + first = false; + } + println(")"); + } + + void display_native_list(const Type* t, void* list) { + if (t->kind() != KIND_LIST) return; + const Type* elt = ((const ListType*)t)->element(); + if (elt == INT) _display_list(list); + else if (elt == SYMBOL) _display_symbol_list(list); + else if (elt == BOOL) _display_list(list); + else if (elt == VOID) _display_list(list); + else if (elt == STRING) _display_native_string_list(list); + } + + i64 _strcmp(const char *a, const char *b) { + while (*a && *b && *a == *b) a ++, b ++; + return *(const unsigned char*)a - *(const unsigned char*)b; + } + + const u8* _read_line() { + string s; + while (_stdin.peek() != '\n') { s += _stdin.read(); } + u8* buf = new u8[s.size() + 1]; + for (u32 i = 0; i < s.size(); i ++) buf[i] = s[i]; + buf[s.size()] = '\0'; + return buf; + } + + void add_native_functions(Object& object) { + add_native_function(object, "_cons", (void*)_cons); + + add_native_function(object, "_strcmp", (void*)_strcmp); + add_native_function(object, "_read_line", (void*)_read_line); + + add_native_function(object, "_display_int", (void*)_display_int); + add_native_function(object, "_display_symbol", (void*)_display_symbol); + add_native_function(object, "_display_bool", (void*)_display_bool); + add_native_function(object, "_display_string", (void*)_display_string); + add_native_function(object, "_display_int_list", (void*)_display_list); + add_native_function(object, "_display_symbol_list", (void*)_display_symbol_list); + add_native_function(object, "_display_bool_list", (void*)_display_list); + add_native_function(object, "_display_string_list", (void*)_display_list); + } +} \ No newline at end of file diff --git a/native.h b/native.h new file mode 100644 index 0000000..c576cbe --- /dev/null +++ b/native.h @@ -0,0 +1,14 @@ +#ifndef BASIL_NATIVE_H +#define BASIL_NATIVE_H + +#include "util/defs.h" +#include "ssa.h" +#include "jasmine/x64.h" + +namespace basil { + void display_native_list(const Type* t, void* list); + void add_native_functions(jasmine::Object& object); + const u8* _read_line(); +} + +#endif \ No newline at end of file diff --git a/parse.cpp b/parse.cpp index 0318949..b89ba47 100644 --- a/parse.cpp +++ b/parse.cpp @@ -1,6 +1,5 @@ #include "parse.h" #include "errors.h" -#include namespace basil { i64 parse_int(const string& s) { @@ -11,11 +10,39 @@ namespace basil { return i; } - Value flatten(Value term) { - return term.is_list() && head(term).is_list() && &term.get_list().head() == &term.get_list().tail() ? head(term) : term; - } + string parse_string(const string& s) { + string t; + const u8* sptr = s.raw(); + sptr ++; // skip initial quote + while (*sptr && *sptr != '\"') { + if (*sptr == '\\') { + sptr ++; + switch (*sptr) { + case 'a': t += '\a'; break; + case 'b': t += '\b'; break; + case 'f': t += '\f'; break; + case 'n': t += '\n'; break; + case 'r': t += '\r'; break; + case 't': t += '\t'; break; + case 'v': t += '\v'; break; + case '0': t += '\0'; break; + case '"': t += '"'; break; + case '\'': t += '\''; break; + case '\\': t += '\\'; break; + case '?': t += '\?'; break; + case '\0': break; + } + sptr ++; + } + t += *(sptr ++); + } + sptr ++; // skip final quote + return t; + } - Value parse_primary(TokenView& view, u32 indent); + Value parse_primary(TokenView& view, u32 indent); + void parse_line(TokenView& view, u32 indent, bool consume_line, + vector& terms); bool out_of_input(TokenView& view) { // returns true if out of input if (view.repl()) { @@ -41,20 +68,22 @@ namespace basil { void parse_block(TokenView& view, vector& terms, u32 prev_indent, u32 indent) { while (view.peek().column > prev_indent) { - terms.push(parse_line(view, indent, false)); - if (view.peek().type == T_NEWLINE - && view.peek().column > prev_indent) view.read(); - if (!view.peek() && out_of_input(view)) return; - } - } - - void parse_continuation(TokenView& view, vector& terms, - u32 prev_indent, u32 indent) { - while (!view.peek() || view.peek().column > prev_indent) { - while (view.peek().type != T_NEWLINE) - terms.push(parse(view, indent)); - if (!view.peek() && out_of_input(view)) return; + if (view.peek().type != T_NEWLINE) + terms.push(parse_line(view, indent, false)); + if (view.peek().type == T_NEWLINE) { + if (view.peek().column <= prev_indent && view.repl()) { + view.read(); + return; + } + else view.read(); + } + if (!view.peek() && (!view.repl() || out_of_input(view))) return; } + if (view.peek().type == T_QUOTE + && view.peek().column == prev_indent) { // continuation + u32 new_indent = view.peek().column; + parse_line(view, new_indent, true, terms); + } } Value apply_op(TokenView& view, Value lhs, Value rhs, TokenType op) { @@ -107,6 +136,11 @@ namespace basil { v.set_location(first); return v; } + case T_STRING: { + Value v(parse_string(view.read().value), STRING); + v.set_location(first); + return v; + } case T_PLUS: { view.read(); Value v = list_of(Value("+"), 0, parse_primary(view, indent)); @@ -161,9 +195,9 @@ namespace basil { view.read(); vector terms; parse_enclosed(view, terms, T_PIPE, indent); - Value v = terms.size() == 1 ? terms[0] : list_of(terms); + Value v = cons(Value("splice"), list_of(terms)); v.set_location(first); - return list_of(Value("splice"), v); + return v; } default: err(view.peek(), "Unexpected token '", @@ -199,29 +233,38 @@ namespace basil { return v; } - Value parse_line(TokenView& view, u32 indent, bool consume_line) { - SourceLocation first = view.peek(); - vector terms; - while (view.peek() && view.peek().type != T_NEWLINE) { - if (view.peek().type == T_COLON) { + void parse_line(TokenView& view, u32 indent, bool consume_line, + vector& terms) { + SourceLocation first = view.peek(); + while (view.peek()) { + if (view.peek().type == T_NEWLINE) { view.read(); - if (view.peek().type == T_NEWLINE) { - view.read(); - if (!view.peek() && out_of_input(view)) return error(); - if (view.peek().column > indent) - parse_block(view, terms, indent, view.peek().column); - return list_of(terms); - } - else { - err(view.peek(), "Unexpected colon in input."); - return error(); - } + if (!view.peek() && (!view.repl() || out_of_input(view))) return; + if (view.peek().column > indent) + parse_block(view, terms, indent, view.peek().column); + else if (view.peek().type == T_QUOTE + && view.peek().column == indent) { // continuation + u32 new_indent = view.peek().column; + parse_line(view, new_indent, true, terms); + } + else if (!consume_line) view.rewind(); + + return; } Value v = parse(view, indent); - if (v.is_error()) return error(); - else terms.push(v); + if (!v.is_error()) terms.push(v); } - if (consume_line) view.read(); + } + + Value parse_line(TokenView& view, u32 indent, bool consume_line) { + SourceLocation first = view.peek(); + vector terms; + if (view.peek().type == T_NEWLINE) { + if (consume_line) view.read(); + return empty(); + } + parse_line(view, indent, consume_line, terms); + Value v = list_of(terms); v.set_location(first); return v; diff --git a/parse.h b/parse.h index 0957cd0..b303ecb 100644 --- a/parse.h +++ b/parse.h @@ -1,9 +1,9 @@ #ifndef BASIL_PARSE_H #define BASIL_PARSE_H -#include "defs.h" +#include "util/defs.h" #include "lex.h" -#include "vec.h" +#include "util/vec.h" #include "values.h" namespace basil { diff --git a/source.cpp b/source.cpp index 31c9664..acd319d 100644 --- a/source.cpp +++ b/source.cpp @@ -1,8 +1,9 @@ #include "source.h" -#include "io.h" +#include "util/io.h" namespace basil { void Source::add_lines(string* s) { + if ((*s)[s->size() - 1] != '\n') *s += '\n'; const u8* p = s->raw(); const u8* start = p; u32 size = 0; @@ -33,8 +34,8 @@ namespace basil { if (!f) return; _sections.push(new string()); - while (f) { - if (f.peek() == '\t') *_sections.back() += " "; + while (f.peek()) { + if (f.peek() == '\t') *_sections.back() += " ", f.read(); else *_sections.back() += f.read(); } @@ -65,6 +66,11 @@ namespace basil { return *this; } + void Source::add_line(const string& text) { + string* line = new string(text); + add_lines(line); + } + const_slice Source::line(u32 i) const { return _lines[i]; } diff --git a/source.h b/source.h index 1b1cd9f..eb0854a 100644 --- a/source.h +++ b/source.h @@ -1,9 +1,9 @@ #ifndef BASIL_SOURCE_H #define BASIL_SOURCE_H -#include "defs.h" -#include "vec.h" -#include "str.h" +#include "util/defs.h" +#include "util/vec.h" +#include "util/str.h" namespace basil { class Source { @@ -18,7 +18,8 @@ namespace basil { ~Source(); Source(const Source& other); Source& operator=(const Source& other); - + + void add_line(const string& text); const_slice line(u32 i) const; const vector>& lines() const; diff --git a/ssa.cpp b/ssa.cpp new file mode 100644 index 0000000..589c153 --- /dev/null +++ b/ssa.cpp @@ -0,0 +1,706 @@ +#include "ssa.h" +#include "util/hash.h" + +namespace basil { + using namespace x64; + + Location ssa_none() { + Location loc; + loc.type = SSA_NONE; + return loc; + } + + Location ssa_immediate(i64 i) { + Location loc; + loc.type = SSA_IMMEDIATE; + loc.immediate = i; + return loc; + } + + Insn::Insn(): + _loc(ssa_none()), _func(nullptr) {} + + Insn::~Insn() { + // + } + + void Insn::setfunc(Function* func) { + _func = func; + } + + Location Insn::loc() { + if (_loc.type == SSA_NONE) _loc = lazy_loc(); + return _loc; + } + + static u32 anonymous_locals = 0; + static u32 anonymous_labels = 0; + static vector all_labels; + static map label_map; + static vector all_locals; + static vector all_constants; + + u32 ssa_find_label(const string& label) { + auto it = label_map.find(label); + if (it == label_map.end()) return ssa_add_label(label); + return it->second; + } + + u32 ssa_add_label(const string& label) { + all_labels.push(label); + label_map[label] = all_labels.size() - 1; + return all_labels.size() - 1; + } + + u32 ssa_next_label() { + buffer b; + write(b, ".L", anonymous_labels ++); + string s; + read(b, s); + all_labels.push(s); + label_map[s] = all_labels.size() - 1; + return all_labels.size() - 1; + } + + Location ssa_next_local(const Type* t) { + buffer b; + write(b, ".t", anonymous_locals ++); + string s; + read(b, s); + all_locals.push({ s, 0, t, {}}); + + Location loc; + loc.type = SSA_LOCAL; + loc.local_index = all_locals.size() - 1; + return loc; + } + + Location ssa_const(u32 label, const string& constant) { + ConstantInfo info; + info.type = STRING; + info.name = all_labels[label]; + for (u32 i = 0; i < constant.size(); i ++) info.data.push(constant[i]); + info.data.push('\0'); + + all_constants.push(info); + Location loc; + loc.type = SSA_CONSTANT; + loc.constant_index = all_constants.size() - 1; + return loc; + } + + Symbol symbol_for_label(u32 label, SymbolLinkage type) { + return type == GLOBAL_SYMBOL ? + global((const char*)all_labels[label].raw()) + : local((const char*)all_labels[label].raw()); + } + + void ssa_emit_constants(Object& object) { + using namespace x64; + writeto(object); + for (const ConstantInfo& info : all_constants) { + label(symbol_for_label(label_map[info.name], GLOBAL_SYMBOL)); + for (u8 b : info.data) object.code().write(b); + } + } + + static map local_state_counts; + + Location ssa_next_local_for(const Location& loc) { + const LocalInfo& info = all_locals[loc.local_index]; + auto it = local_state_counts.find(info.name); + if (it == local_state_counts.end()) { + LocalInfo new_info = { info.name, 0, info.type, info.value }; + all_locals.push(new_info); + Location loc; + loc.type = SSA_LOCAL; + loc.local_index = all_locals.size() - 1; + local_state_counts[info.name] = 1; + return loc; + } + else { + u32 num = it->second; + LocalInfo new_info = info; + new_info.index = num; + all_locals.push(new_info); + Location loc; + loc.type = SSA_LOCAL; + loc.local_index = all_locals.size() - 1; + it->second ++; + return loc; + } + } + + const Type* ssa_type(const Location& loc) { + switch (loc.type) { + case SSA_NONE: + return VOID; + case SSA_LOCAL: + return all_locals[loc.local_index].type; + case SSA_CONSTANT: + return all_constants[loc.constant_index].type; + case SSA_IMMEDIATE: + return INT; // close enough at this stage + case SSA_LABEL: + return INT; // ...close enough :p + } + } + + x64::Arg x64_arg(const Location& loc) { + switch (loc.type) { + case SSA_NONE: + return imm(0); + case SSA_LOCAL: + return all_locals[loc.local_index].value; + case SSA_CONSTANT: + return label64( + global((const char*)all_constants[loc.constant_index].name.raw())); + case SSA_IMMEDIATE: + return imm(loc.immediate); + case SSA_LABEL: + return label64(global((const char*)all_labels[loc.label_index].raw())); + } + } + + Function::Function(u32 label): + _stack(0), _label(label) {} + + Function::Function(const string& label): + _stack(0), _label(ssa_add_label(label)) {} + + Location Function::create_local(const Type* t) { + Location l = ssa_next_local(t); + _locals.push(l); + return l; + } + + Function::~Function() { + for (Insn* i : _insns) delete i; + for (Function* f : _fns) delete f; + } + + void Function::place_label(u32 label) { + _labels[label] = _insns.size(); + } + + Function& Function::create_function() { + _fns.push(new Function(ssa_next_label())); + return *_fns.back(); + } + + Function& Function::create_function(const string& name) { + _fns.push(new Function(name)); + return *_fns.back(); + } + + Location Function::create_local(const string& name, const Type* t) { + LocalInfo info = { name, 0, t, {}}; + all_locals.push(info); + local_state_counts[name] = 1; + + Location loc; + loc.type = SSA_LOCAL; + loc.local_index = all_locals.size() - 1; + _locals.push(loc); + return loc; + } + + Location Function::next_local(const Location& loc) { + Location next = ssa_next_local_for(loc); + _locals.push(next); + return next; + } + + Location Function::add(Insn* insn) { + insn->setfunc(this); + _insns.push(insn); + return insn->loc(); + } + + u32 Function::label() const { + return _label; + } + + void Function::allocate() { + for (Function* fn : _fns) fn->allocate(); + for (Location l : _locals) { + LocalInfo& info = all_locals[l.local_index]; + info.value = x64::m64(RBP, -(_stack += 8)); // assumes everything is a word + } + } + + void Function::emit(Object& obj) { + for (Function* fn : _fns) fn->emit(obj); + + writeto(obj); + Symbol label = global((const char*)all_labels[_label].raw()); + x64::label(label); + push(r64(RBP)); + mov(r64(RBP), r64(RSP)); + sub(r64(RSP), imm(_stack)); + + for (Insn* i : _insns) i->emit(); + + mov(r64(RSP), r64(RBP)); + pop(r64(RBP)); + ret(); + } + + void Function::format(stream& io) const { + for (Function* fn : _fns) fn->format(io); + writeln(io, all_labels[_label], ":"); + for (Insn* i : _insns) writeln(io, " ", i); + } + + Location LoadInsn::lazy_loc() { + return _func->create_local(ssa_type(_src)); + } + + LoadInsn::LoadInsn(Location src): + _src(src) {} + + void LoadInsn::emit() { + auto temp = r64(RAX), src = x64_arg(_src), dst = x64_arg(_loc); + mov(temp, src); + mov(dst, temp); + } + + void LoadInsn::format(stream& io) const { + write(io, _loc, " = ", _src); + } + + Location StoreInsn::lazy_loc() { + if (_init) return _dest; + else return _func->next_local(_dest); + } + + StoreInsn::StoreInsn(Location dest, Location src, bool init): + _dest(dest), _src(src), _init(init) {} + + void StoreInsn::emit() { + auto temp = r64(RAX), src = x64_arg(_src), dst = x64_arg(_dest); + mov(temp, src); + mov(dst, temp); + } + + void StoreInsn::format(stream& io) const { + write(io, _loc, " = ", _src); + } + + LoadPtrInsn::LoadPtrInsn(Location src, const Type* t, i32 offset): + _src(src), _type(t), _offset(offset) {} + + Location LoadPtrInsn::lazy_loc() { + return _func->create_local(_type); + } + + void LoadPtrInsn::emit() { + auto src = x64_arg(_src), dst = x64_arg(_loc); + mov(r64(RAX), src); + mov(r64(RDX), m64(RAX, _offset)); + mov(dst, r64(RDX)); + } + + void LoadPtrInsn::format(stream& io) const { + write(io, _loc, " = *", _src); + } + + StorePtrInsn::StorePtrInsn(Location dest, Location src, i32 offset): + _dest(dest), _src(src), _offset(offset) {} + + Location StorePtrInsn::lazy_loc() { + return ssa_none(); + } + + void StorePtrInsn::emit() { + auto src = x64_arg(_src), dst = x64_arg(_dest); + mov(r64(RAX), dst); + mov(r64(RDX), src); + mov(m64(RAX, _offset), r64(RDX)); + } + + void StorePtrInsn::format(stream& io) const { + write(io, "*", _dest, " = ", _src); + } + + AddressInsn::AddressInsn(Location src, const Type* t): + _src(src), _type(t) {} + + Location AddressInsn::lazy_loc() { + return _func->create_local(_type); + } + + void AddressInsn::emit() { + auto src = x64_arg(_src), dst = x64_arg(_loc); + lea(r64(RAX), src); + mov(dst, r64(RAX)); + } + + void AddressInsn::format(stream& io) const { + write(io, _loc, " = &", _src); + } + + void emit_binary(void(*op)(const x64::Arg&, const x64::Arg&, x64::Size), + Location dst, Location left, Location right, x64::Size size = AUTO) { + auto temp = r64(RAX), _left = x64_arg(left), + _right = x64_arg(right), _dst = x64_arg(dst); + mov(temp, _left); + op(temp, _right, size); + mov(_dst, temp); + } + + void emit_compare(x64::Condition cond, Location dst, + Location left, Location right) { + auto temp = r64(RAX), _left = x64_arg(left), + _right = x64_arg(right), _dst = x64_arg(dst); + mov(temp, _left); + cmp(temp, _right); + if (is_memory(_dst.type)) { + mov(temp, imm(0)); + setcc(temp, cond); + mov(_dst, temp); + } + else { + mov(_dst, imm(0)); + setcc(_dst, cond); + } + } + + BinaryInsn::BinaryInsn(const char* name, Location left, + Location right): + _name(name), _left(left), _right(right) {} + + void BinaryInsn::format(stream& io) const { + write(io, _loc, " = ", _left, " ", _name, " ", _right); + } + + Location BinaryMathInsn::lazy_loc() { + return _func->create_local(ssa_type(_left)); + } + + BinaryMathInsn::BinaryMathInsn(const char* name, Location left, + Location right): + BinaryInsn(name, left, right) {} + + AddInsn::AddInsn(Location left, Location right): + BinaryMathInsn("+", left, right) {} + + void AddInsn::emit() { + emit_binary(add, _loc, _left, _right); + } + + SubInsn::SubInsn(Location left, Location right): + BinaryMathInsn("-", left, right) {} + + void SubInsn::emit() { + emit_binary(sub, _loc, _left, _right); + } + + MulInsn::MulInsn(Location left, Location right): + BinaryMathInsn("*", left, right) {} + + void MulInsn::emit() { + auto temp = r64(RAX), left = x64_arg(_left), + right = x64_arg(_right), dst = x64_arg(_loc); + mov(temp, left); + if (_right.type == SSA_IMMEDIATE) { + mov(r64(RDX), right); + imul(temp, r64(RDX)); + } + else imul(temp, right); + mov(dst, temp); + } + + DivInsn::DivInsn(Location left, Location right): + BinaryMathInsn("/", left, right) {} + + void DivInsn::emit() { + auto rax = r64(RAX), rcx = r64(RCX), rdx = r64(RDX), + left = x64_arg(_left), right = x64_arg(_right), + dst = x64_arg(_loc); + mov(rax, left); + cdq(); + if (_right.type == SSA_IMMEDIATE) { + mov(rcx, right); + idiv(rcx); + } + else idiv(right, QWORD); + mov(dst, rax); + } + + RemInsn::RemInsn(Location left, Location right): + BinaryMathInsn("%", left, right) {} + + void RemInsn::emit() { + auto rax = r64(RAX), rcx = r64(RCX), rdx = r64(RDX), + left = x64_arg(_left), right = x64_arg(_right), + dst = x64_arg(_loc); + mov(rax, left); + cdq(); + if (_right.type == SSA_IMMEDIATE) { + mov(rcx, right); + idiv(rcx); + } + else idiv(right, QWORD); + mov(dst, rdx); + } + + BinaryLogicInsn::BinaryLogicInsn(const char* name, Location left, + Location right): + BinaryInsn(name, left, right) {} + + Location BinaryLogicInsn::lazy_loc() { + return _func->create_local(BOOL); + } + + AndInsn::AndInsn(Location left, Location right): + BinaryLogicInsn("and", left, right) {} + + void AndInsn::emit() { + emit_binary(and_, _loc, _left, _right); + } + + OrInsn::OrInsn(Location left, Location right): + BinaryLogicInsn("or", left, right) {} + + void OrInsn::emit() { + emit_binary(or_, _loc, _left, _right); + } + + XorInsn::XorInsn(Location left, Location right): + BinaryLogicInsn("xor", left, right) {} + + void XorInsn::emit() { + emit_binary(xor_, _loc, _left, _right); + } + + NotInsn::NotInsn(Location src): + _src(src) {} + + Location NotInsn::lazy_loc() { + return _func->create_local(BOOL); + } + + void NotInsn::emit() { + auto src = x64_arg(_src), dst = x64_arg(_loc); + xor_(r64(RDX), r64(RDX)); + mov(r64(RAX), src); + cmp(r64(RAX), imm(0)); + setcc(r64(RDX), ZERO); + mov(dst, r64(RDX)); + } + + void NotInsn::format(stream& io) const { + write(io, _loc, " = ", "not ", _src); + } + + BinaryEqualityInsn::BinaryEqualityInsn(const char* name, + Location left, Location right): + BinaryInsn(name, left, right) {} + + Location BinaryEqualityInsn::lazy_loc() { + return _func->create_local(BOOL); + } + + EqualInsn::EqualInsn(Location left, Location right): + BinaryEqualityInsn("==", left, right) {} + + void EqualInsn::emit() { + emit_compare(EQUAL, _loc, _left, _right); + } + + InequalInsn::InequalInsn(Location left, Location right): + BinaryEqualityInsn("!=", left, right) {} + + void InequalInsn::emit() { + emit_compare(NOT_EQUAL, _loc, _left, _right); + } + + BinaryRelationInsn::BinaryRelationInsn(const char* name, + Location left, Location right): + BinaryInsn(name, left, right) {} + + Location BinaryRelationInsn::lazy_loc() { + return _func->create_local(BOOL); + } + + LessInsn::LessInsn(Location left, Location right): + BinaryRelationInsn("<", left, right) {} + + void LessInsn::emit() { + emit_compare(LESS, _loc, _left, _right); + } + + LessEqualInsn::LessEqualInsn(Location left, Location right): + BinaryRelationInsn("<=", left, right) {} + + void LessEqualInsn::emit() { + emit_compare(LESS_OR_EQUAL, _loc, _left, _right); + } + + GreaterInsn::GreaterInsn(Location left, Location right): + BinaryRelationInsn(">", left, right) {} + + void GreaterInsn::emit() { + emit_compare(GREATER, _loc, _left, _right); + } + + GreaterEqualInsn::GreaterEqualInsn(Location left, Location right): + BinaryRelationInsn(">=", left, right) {} + + void GreaterEqualInsn::emit() { + emit_compare(GREATER_OR_EQUAL, _loc, _left, _right); + } + + Location RetInsn::lazy_loc() { + return ssa_none(); + } + + RetInsn::RetInsn(Location src): + _src(src) {} + + void RetInsn::emit() { + auto rax = r64(RAX), src = x64_arg(_src); + mov(rax, src); + } + + void RetInsn::format(stream& io) const { + write(io, "return ", _src); + } + + static const x64::Register X64_ARG_REGISTERS[] = { + RDI, RSI, RDX, RCX, R8, R9 + }; + + LoadArgumentInsn::LoadArgumentInsn(u32 index, const Type* type): + _index(index), _type(type) {} + + Location LoadArgumentInsn::lazy_loc() { + return _func->create_local(_type); + } + + void LoadArgumentInsn::emit() { + mov(x64_arg(_loc), r64(X64_ARG_REGISTERS[_index])); + } + + void LoadArgumentInsn::format(stream& io) const { + write(io, _loc, " = $", _index); + } + + StoreArgumentInsn::StoreArgumentInsn(Location src, u32 index, const Type* type): + _src(src), _index(index), _type(type) {} + + Location StoreArgumentInsn::lazy_loc() { + return ssa_none(); + } + + void StoreArgumentInsn::emit() { + mov(r64(X64_ARG_REGISTERS[_index]), x64_arg(_src)); + } + + void StoreArgumentInsn::format(stream& io) const { + write(io, "$", _index, " = ", _src); + } + + CallInsn::CallInsn(u32 label, const Type* ret): + _label(label), _ret(ret) {} + + Location CallInsn::lazy_loc() { + return _func->create_local(_ret); + } + + void CallInsn::emit() { + call(label64(symbol_for_label(_label, GLOBAL_SYMBOL))); + mov(x64_arg(_loc), r64(RAX)); + } + + void CallInsn::format(stream& io) const { + write(io, _loc, " = ", all_labels[_label], "()"); + } + + Label::Label(u32 label): + _label(label) {} + + Location Label::lazy_loc() { + return ssa_none(); + } + + void Label::emit() { + label(symbol_for_label(_label, LOCAL_SYMBOL)); + } + + void Label::format(stream& io) const { + write(io, "\b\b\b\b", all_labels[_label], ":"); + } + + GotoInsn::GotoInsn(u32 label): + _label(label) {} + + Location GotoInsn::lazy_loc() { + return ssa_none(); + } + + void GotoInsn::emit() { + jmp(label64(symbol_for_label(_label, LOCAL_SYMBOL))); + } + + void GotoInsn::format(stream& io) const { + write(io, "goto ", all_labels[_label]); + } + + IfZeroInsn::IfZeroInsn(u32 label, Location cond): + _label(label), _cond(cond) {} + + Location IfZeroInsn::lazy_loc() { + return ssa_none(); + } + + void IfZeroInsn::emit() { + auto cond = x64_arg(_cond); + if (is_immediate(cond.type)) { + mov(r64(RAX), cond); + cmp(r64(RAX), imm(0)); + } + else cmp(x64_arg(_cond), imm(0)); + jcc(label64(symbol_for_label(_label, LOCAL_SYMBOL)), EQUAL); + } + + void IfZeroInsn::format(stream& io) const { + write(io, "if not ", _cond, " goto ", all_labels[_label]); + } +} + +void write(stream& io, const basil::Location& loc) { + switch (loc.type) { + case basil::SSA_NONE: + write(io, "none"); + return; + case basil::SSA_LOCAL: + write(io, basil::all_locals[loc.local_index].name); + if (basil::all_locals[loc.local_index].index > 0 + || basil::all_locals[loc.local_index].name[0] != '.') + write(io, ".", basil::all_locals[loc.local_index].index); + return; + case basil::SSA_IMMEDIATE: + write(io, loc.immediate); + return; + case basil::SSA_LABEL: + write(io, basil::all_labels[loc.label_index]); + case basil::SSA_CONSTANT: + write(io, basil::all_constants[loc.constant_index].name); + default: + return; + } +} + +void write(stream& io, basil::Insn* insn) { + insn->format(io); +} + +void write(stream& io, const basil::Insn* insn) { + insn->format(io); +} + +void write(stream& io, const basil::Function& func) { + func.format(io); +} \ No newline at end of file diff --git a/ssa.h b/ssa.h new file mode 100644 index 0000000..6ecfbe9 --- /dev/null +++ b/ssa.h @@ -0,0 +1,391 @@ +#ifndef BASIL_SSA_H +#define BASIL_SSA_H + +#include "util/defs.h" +#include "util/str.h" +#include "util/vec.h" +#include "util/io.h" +#include "type.h" + +#include "jasmine/x64.h" + +namespace basil { + using namespace jasmine; + + enum LocationType { + SSA_NONE, + SSA_LOCAL, + SSA_IMMEDIATE, + SSA_CONSTANT, + SSA_LABEL + }; + + struct LocalInfo { + string name; + u32 index; + const Type* type; + x64::Arg value; + }; + + struct ConstantInfo { + string name; + vector data; + const Type* type; + x64::Arg value; + }; + + struct Location { + union { + u32 local_index; + i64 immediate; + u32 constant_index; + u32 label_index; + }; + LocationType type; + + const Type* value_type(); + }; + + Location ssa_none(); + Location ssa_immediate(i64 i); + const Type* ssa_type(const Location& loc); + x64::Arg x64_arg(const Location& loc); + + class Function; + + class Insn { + protected: + Function* _func; + Location _loc; + virtual Location lazy_loc() = 0; + + friend class Function; + void setfunc(Function* func); + public: + Insn(); + virtual ~Insn(); + + Location loc(); + virtual void emit() = 0; + virtual void format(stream& io) const = 0; + }; + + u32 ssa_find_label(const string& label); + u32 ssa_add_label(const string& label); + u32 ssa_next_label(); + Location ssa_next_local(const Type* t); + Location ssa_const(u32 label, const string& constant); + void ssa_emit_constants(Object& object); + + class Function { + vector _fns; + vector _insns; + i64 _stack; + vector _locals; + map _labels; + u32 _label; + Function(u32 label); + public: + Function(const string& label); + ~Function(); + + void place_label(u32 label); + Function& create_function(); + Function& create_function(const string& name); + Location create_local(const Type* t); + Location create_local(const string& name, const Type* t); + Location next_local(const Location& loc); + Location add(Insn* insn); + u32 label() const; + void allocate(); + void emit(Object& obj); + void format(stream& io) const; + }; + + class LoadInsn : public Insn { + Location _src; + protected: + Location lazy_loc() override; + public: + LoadInsn(Location src); + + void emit() override; + void format(stream& io) const override; + }; + + class StoreInsn : public Insn { + Location _dest, _src; + bool _init; + protected: + Location lazy_loc() override; + public: + StoreInsn(Location dest, Location src, bool init); + + void emit() override; + void format(stream& io) const override; + }; + + class LoadPtrInsn : public Insn { + Location _src; + const Type* _type; + i32 _offset; + protected: + Location lazy_loc() override; + public: + LoadPtrInsn(Location src, const Type* t, i32 offset); + + void emit() override; + void format(stream& io) const override; + }; + + class StorePtrInsn : public Insn { + Location _dest, _src; + i32 _offset; + protected: + Location lazy_loc() override; + public: + StorePtrInsn(Location dest, Location src, i32 offset); + + void emit() override; + void format(stream& io) const override; + }; + + class AddressInsn : public Insn { + Location _src; + const Type* _type; + protected: + Location lazy_loc() override; + public: + AddressInsn(Location src, const Type* t); + + void emit() override; + void format(stream& io) const override; + }; + + class BinaryInsn : public Insn { + const char* _name; + protected: + Location _left, _right; + public: + BinaryInsn(const char* name, Location left, Location right); + + void format(stream& io) const override; + }; + + class BinaryMathInsn : public BinaryInsn { + protected: + Location lazy_loc() override; + public: + BinaryMathInsn(const char* name, Location left, + Location right); + }; + + class AddInsn : public BinaryMathInsn { + public: + AddInsn(Location left, Location right); + void emit() override; + }; + + class SubInsn : public BinaryMathInsn { + public: + SubInsn(Location left, Location right); + void emit() override; + }; + + class MulInsn : public BinaryMathInsn { + public: + MulInsn(Location left, Location right); + void emit() override; + }; + + class DivInsn : public BinaryMathInsn { + public: + DivInsn(Location left, Location right); + void emit() override; + }; + + class RemInsn : public BinaryMathInsn { + public: + RemInsn(Location left, Location right); + void emit() override; + }; + + class BinaryLogicInsn : public BinaryInsn { + protected: + Location lazy_loc() override; + public: + BinaryLogicInsn(const char* name, Location left, + Location right); + }; + + class AndInsn : public BinaryLogicInsn { + public: + AndInsn(Location left, Location right); + void emit() override; + }; + + class OrInsn : public BinaryLogicInsn { + public: + OrInsn(Location left, Location right); + void emit() override; + }; + + class XorInsn : public BinaryLogicInsn { + public: + XorInsn(Location left, Location right); + void emit() override; + }; + + class NotInsn : public Insn { + Location _src; + protected: + Location lazy_loc() override; + public: + NotInsn(Location src); + + void emit() override; + void format(stream& io) const override; + }; + + class BinaryEqualityInsn : public BinaryInsn { + protected: + Location lazy_loc() override; + public: + BinaryEqualityInsn(const char* name, Location left, + Location right); + }; + + class EqualInsn : public BinaryEqualityInsn { + public: + EqualInsn(Location left, Location right); + void emit() override; + }; + + class InequalInsn : public BinaryEqualityInsn { + public: + InequalInsn(Location left, Location right); + void emit() override; + }; + + class BinaryRelationInsn : public BinaryInsn { + protected: + Location lazy_loc() override; + public: + BinaryRelationInsn(const char* name, Location left, + Location right); + }; + + class LessInsn : public BinaryRelationInsn { + public: + LessInsn(Location left, Location right); + void emit() override; + }; + + class LessEqualInsn : public BinaryRelationInsn { + public: + LessEqualInsn(Location left, Location right); + void emit() override; + }; + + class GreaterInsn : public BinaryRelationInsn { + public: + GreaterInsn(Location left, Location right); + void emit() override; + }; + + class GreaterEqualInsn : public BinaryRelationInsn { + public: + GreaterEqualInsn(Location left, Location right); + void emit() override; + }; + + class RetInsn : public Insn { + Location _src; + protected: + Location lazy_loc() override; + public: + RetInsn(Location src); + + void emit() override; + void format(stream& io) const override; + }; + + class LoadArgumentInsn : public Insn { + u32 _index; + const Type* _type; + protected: + Location lazy_loc() override; + public: + LoadArgumentInsn(u32 index, const Type* type); + + void emit() override; + void format(stream& io) const override; + }; + + class StoreArgumentInsn : public Insn { + Location _src; + u32 _index; + const Type* _type; + protected: + Location lazy_loc() override; + public: + StoreArgumentInsn(Location src, u32 index, const Type* type); + + void emit() override; + void format(stream& io) const override; + }; + + class CallInsn : public Insn { + u32 _label; + const Type* _ret; + protected: + Location lazy_loc() override; + public: + CallInsn(u32 label, const Type* ret); + + void emit() override; + void format(stream& io) const override; + }; + + class Label : public Insn { + u32 _label; + protected: + Location lazy_loc() override; + public: + Label(u32 label); + + void emit() override; + void format(stream& io) const override; + }; + + class GotoInsn : public Insn { + u32 _label; + protected: + Location lazy_loc() override; + public: + GotoInsn(u32 label); + + void emit() override; + void format(stream& io) const override; + }; + + class IfZeroInsn : public Insn { + u32 _label; + Location _cond; + const Type* _result; + protected: + Location lazy_loc() override; + public: + IfZeroInsn(u32 label, Location cond); + + void emit() override; + void format(stream& io) const override; + }; +} + +void write(stream& io, const basil::Location& loc); +void write(stream& io, basil::Insn* insn); +void write(stream& io, const basil::Insn* insn); +void write(stream& io, const basil::Function& func); + +#endif \ No newline at end of file diff --git a/std/control.bl b/std/control.bl new file mode 100644 index 0000000..4c988e7 --- /dev/null +++ b/std/control.bl @@ -0,0 +1,7 @@ +macro (for name :in list expr) + def l |list| + def |name| l head + while l != [] + |name| = l head + |expr| + l = l tail \ No newline at end of file diff --git a/std/list.bl b/std/list.bl new file mode 100644 index 0000000..987efd8 --- /dev/null +++ b/std/list.bl @@ -0,0 +1,40 @@ +infix (list length) + if list empty? 0 else (list tail length) + 1 + +def (append a b) + if a empty? b else a.head :: (append a.tail b) + +infix (list take n) + if n == 0 or list empty? [] + :else list.head :: (list.tail take n - 1) + +infix (list drop n) + if list empty? [] + :elif n == 0 list + :else list.tail drop n - 1 + +def (reverse list) + reverse-internal list [] + +def (reverse-internal list acc) + if list empty? acc + :else (reverse-internal list.tail list.head :: acc) + +def (merge a b) + if a empty? b + :elif b empty? a + :elif a.head < b.head + a.head :: (merge a.tail b) + :else + b.head :: (merge a b.tail) + +def (sort list) + if list empty? + [] + :elif list tail empty? + list + :else + def half (list length / 2) + def front (list take half) + def back (list drop half) + merge (sort front) (sort back) \ No newline at end of file diff --git a/type.cpp b/type.cpp index 05b32f7..2f09a3e 100644 --- a/type.cpp +++ b/type.cpp @@ -8,6 +8,10 @@ namespace basil { return _hash; } + bool Type::concrete() const { + return this != ANY; + } + SingletonType::SingletonType(const string& repr) : Type(::hash(repr)), _repr(repr) {} TypeKind SingletonType::kind() const { @@ -25,6 +29,10 @@ namespace basil { ListType::ListType(const Type* element): Type(element->hash() ^ 11340086872871314823ul), _element(element) {} + bool ListType::concrete() const { + return _element->concrete(); + } + const Type* ListType::element() const { return _element; } @@ -183,6 +191,75 @@ namespace basil { write(io, "macro(", _arity, ")"); } + RuntimeType::RuntimeType(const Type* base): + Type(base->hash() ^ 5857490642180150551ul), _base(base) {} + + const Type* RuntimeType::base() const { + return _base; + } + + TypeKind RuntimeType::kind() const { + return KIND_RUNTIME; + } + + bool RuntimeType::operator==(const Type& other) const { + return other.kind() == kind() && + *((const RuntimeType&)other).base() == *base(); + } + + void RuntimeType::format(stream& io) const { + write(io, "runtime<", _base, ">"); + } + + static vector type_variables; + static vector typevar_names; + + static string next_typevar() { + buffer b; + write(b, "'T", type_variables.size()); + string s; + read(b, s); + return s; + } + + static u32 create_typevar() { + type_variables.push(ANY); + typevar_names.push(next_typevar()); + return type_variables.size() - 1; + } + + TypeVariable::TypeVariable(u32 id): + Type(::hash(id) ^ 3860592187614349697ul), _id(id) {} + + TypeVariable::TypeVariable(): + TypeVariable(create_typevar()) {} + + const Type* TypeVariable::actual() const { + return type_variables[_id]; + } + + void TypeVariable::bind(const Type* concrete) const { + type_variables[_id] = concrete; + } + + bool TypeVariable::concrete() const { + return type_variables[_id]->concrete(); + } + + TypeKind TypeVariable::kind() const { + return KIND_TYPEVAR; + } + + bool TypeVariable::operator==(const Type& other) const { + return other.kind() == kind() && _id == ((const TypeVariable&)other)._id; + } + + void TypeVariable::format(stream& io) const { + write(io, typevar_names[_id]); + if (type_variables[_id]->concrete()) + write(io, "(", type_variables[_id], ")"); + } + struct TypeBox { const Type* t; @@ -210,7 +287,38 @@ namespace basil { *ERROR = find("error"), *TYPE = find("type"), *ALIAS = find(), - *BOOL = find("bool"); + *BOOL = find("bool"), + *ANY = find("any"), + *STRING = find("string"); + + const Type* unify(const Type* a, const Type* b) { + if (!a || !b) return nullptr; + + if (a == ANY) return b; + else if (b == ANY) return a; + + if (a->kind() == KIND_TYPEVAR) { + if (!((const TypeVariable*)a)->actual()->concrete()) + return ((const TypeVariable*)a)->bind(b), b; + a = ((const TypeVariable*)a)->actual(); + } + + if (b->kind() == KIND_TYPEVAR) { + if (!((const TypeVariable*)b)->actual()->concrete()) + return ((const TypeVariable*)b)->bind(a), a; + b = ((const TypeVariable*)b)->actual(); + } + + if (a->kind() == KIND_LIST && b->kind() == KIND_LIST) + return find(unify(((const ListType*)a)->element(), + ((const ListType*)b)->element())); + + if (a == VOID && b->kind() == KIND_LIST) return b; + if (b == VOID && a->kind() == KIND_LIST) return a; + + if (a != b) return nullptr; + return a; + } } template<> diff --git a/type.h b/type.h index 471e8a7..40a0e1a 100644 --- a/type.h +++ b/type.h @@ -1,23 +1,25 @@ #ifndef BASIL_TYPE_H #define BASIL_TYPE_H -#include "defs.h" -#include "str.h" -#include "hash.h" -#include "io.h" -#include "vec.h" +#include "util/defs.h" +#include "util/str.h" +#include "util/hash.h" +#include "util/io.h" +#include "util/vec.h" namespace basil { const u8 GC_KIND_FLAG = 128; enum TypeKind : u8 { KIND_SINGLETON = 0, + KIND_TYPEVAR = 1, KIND_LIST = GC_KIND_FLAG | 0, KIND_SUM = GC_KIND_FLAG | 1, KIND_PRODUCT = GC_KIND_FLAG | 2, KIND_FUNCTION = GC_KIND_FLAG | 3, KIND_ALIAS = GC_KIND_FLAG | 4, - KIND_MACRO = GC_KIND_FLAG | 5 + KIND_MACRO = GC_KIND_FLAG | 5, + KIND_RUNTIME = GC_KIND_FLAG | 6 }; class Type { @@ -28,6 +30,7 @@ namespace basil { public: virtual TypeKind kind() const = 0; u64 hash() const; + virtual bool concrete() const; virtual bool operator==(const Type& other) const = 0; virtual void format(stream& io) const = 0; }; @@ -48,6 +51,7 @@ namespace basil { ListType(const Type* element); const Type* element() const; + bool concrete() const override; TypeKind kind() const override; bool operator==(const Type& other) const override; void format(stream& io) const override; @@ -110,6 +114,32 @@ namespace basil { void format(stream& io) const override; }; + class RuntimeType : public Type { + const Type* _base; + public: + RuntimeType(const Type* base); + + const Type* base() const; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + void format(stream& io) const override; + }; + + class TypeVariable : public Type { + u32 _id; + protected: + TypeVariable(u32 id); + public: + TypeVariable(); + + const Type* actual() const; + void bind(const Type* concrete) const; + bool concrete() const override; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + void format(stream& io) const override; + }; + const Type* find_existing_type(const Type* t); const Type* create_type(const Type* t); @@ -123,7 +153,9 @@ namespace basil { } extern const Type *INT, *SYMBOL, *VOID, *ERROR, *TYPE, - *ALIAS, *BOOL; + *ALIAS, *BOOL, *ANY, *STRING; + + const Type* unify(const Type* a, const Type* b); } template<> diff --git a/util/README.md b/util/README.md new file mode 100644 index 0000000..a4a622b --- /dev/null +++ b/util/README.md @@ -0,0 +1,18 @@ +# Utilities + +All of the following was implemented before the jam. None of Basil's +primary functionality is contained within these files, they simply +provide an independent implementation of classes in the C++ standard +library. + +Most of these files are taken from [https://github.com/elucent/collections](https://github.com/elucent/collections). + +| File | Contents | +|---|---| +| `defs.h` | A few shared typedefs and forward declarations. | +| `hash.h/cpp` | A standard polymorphic hash function, hash set, and hash map based on robin hood probing. | +| `io.h/cpp` | A suite of variadic io functions and a stream abstraction, which is implemented both by a file wrapper class and an in-memory buffer. | +| `slice.h` | A slice type, for representing subranges of strings and containers. | +| `str.h/cpp` | A resizable byte string type. implements small string optimization and only allocates for strings greater than 15 characters. | +| `rc.h/cpp` | A reference-counted smart pointer type and an embeddable reference-counting interface for other classes. | +| `vec.h` | A resizable vector type. | \ No newline at end of file diff --git a/defs.h b/util/defs.h similarity index 75% rename from defs.h rename to util/defs.h index b327d7a..2de9706 100644 --- a/defs.h +++ b/util/defs.h @@ -32,6 +32,14 @@ typedef int64_t i64; #define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */ #define BOLDCYAN "\033[1m\033[36m" /* Bold Cyan */ #define BOLDWHITE "\033[1m\033[37m" /* Bold White */ +#define ITALICBLACK "\033[3m\033[30m" /* Italic Black */ +#define ITALICRED "\033[3m\033[31m" /* Italic Red */ +#define ITALICGREEN "\033[3m\033[32m" /* Italic Green */ +#define ITALICYELLOW "\033[3m\033[33m" /* Italic Yellow */ +#define ITALICBLUE "\033[3m\033[34m" /* Italic Blue */ +#define ITALICMAGENT "\033[3m\033[35m" /* Italic Magenta */ +#define ITALICCYAN "\033[3m\033[36m" /* Italic Cyan */ +#define ITALICWHITE "\033[3m\033[37m" /* Italic White */ // hash.h @@ -72,22 +80,4 @@ class ustring; template class vector; -namespace basil { - struct Definition; - class Env; - - class Value; - class ListValue; - class SumValue; - class ProductValue; - class FunctionValue; - - class Type; - class SingletonType; - class ListType; - class SumType; - class ProductType; - class FunctionType; -} - #endif \ No newline at end of file diff --git a/hash.cpp b/util/hash.cpp similarity index 100% rename from hash.cpp rename to util/hash.cpp diff --git a/hash.h b/util/hash.h similarity index 100% rename from hash.h rename to util/hash.h diff --git a/io.cpp b/util/io.cpp similarity index 98% rename from io.cpp rename to util/io.cpp index 71ff4b7..aeef50a 100644 --- a/io.cpp +++ b/util/io.cpp @@ -13,12 +13,12 @@ file::file(const char* fname, const char* flags): // } -file::file(FILE* f_in): f(f_in), done(false) { +file::file(FILE* f_in): f(f_in), done(!f) { // } file::~file() { - fclose(f); + if (f) fclose(f); } void file::write(u8 c) { @@ -45,6 +45,7 @@ void file::unget(u8 c) { } file::operator bool() const { + if (!f) return false; int i = fgetc(f); ungetc(i, f); return i != EOF; diff --git a/io.h b/util/io.h similarity index 100% rename from io.h rename to util/io.h diff --git a/rc.cpp b/util/rc.cpp similarity index 100% rename from rc.cpp rename to util/rc.cpp diff --git a/rc.h b/util/rc.h similarity index 98% rename from rc.h rename to util/rc.h index 848bdd2..acf93d9 100644 --- a/rc.h +++ b/util/rc.h @@ -39,6 +39,7 @@ class ref { if (other._value) other._value->count ++; if (_value && !--_value->count) delete _value; _value = other._value; + return *this; } const T& operator*() const { diff --git a/slice.h b/util/slice.h similarity index 100% rename from slice.h rename to util/slice.h diff --git a/str.cpp b/util/str.cpp similarity index 100% rename from str.cpp rename to util/str.cpp diff --git a/str.h b/util/str.h similarity index 100% rename from str.h rename to util/str.h diff --git a/vec.h b/util/vec.h similarity index 100% rename from vec.h rename to util/vec.h diff --git a/values.cpp b/values.cpp index f53336d..c54f1fd 100644 --- a/values.cpp +++ b/values.cpp @@ -1,7 +1,9 @@ #include "values.h" -#include "vec.h" +#include "util/vec.h" #include "env.h" #include "eval.h" +#include "ast.h" +#include "native.h" namespace basil { static map symbol_table; @@ -34,7 +36,8 @@ namespace basil { Value::Value(const string& s, const Type* type): _type(type) { - _data.u = symbol_value(s); + if (type == SYMBOL) _data.u = symbol_value(s); + else if (type == STRING) _data.rc = new StringValue(s); } Value::Value(const Type* type_value, const Type* type): @@ -60,7 +63,7 @@ namespace basil { } Value::Value(FunctionValue* f): - _type(find(INT, INT)) { + _type(find(find(), find())) { _data.rc = f; } @@ -74,6 +77,11 @@ namespace basil { _data.rc = m; } + Value::Value(ASTNode* n): + _type(find(n->type())) { + _data.rc = n; + } + Value::~Value() { if (_type->kind() & GC_KIND_FLAG) _data.rc->dec(); } @@ -85,7 +93,7 @@ namespace basil { } Value& Value::operator=(const Value& other) { - if (other.type()->kind() & GC_KIND_FLAG) other._data.rc->inc(); + if (other.type()->kind() & GC_KIND_FLAG) other._data.rc->inc(); if (_type->kind() & GC_KIND_FLAG) _data.rc->dec(); _type = other._type; _loc = other._loc; @@ -117,6 +125,18 @@ namespace basil { return _data.u; } + bool Value::is_string() const { + return _type == STRING; + } + + const string& Value::get_string() const { + return ((const StringValue*)_data.rc)->value(); + } + + string& Value::get_string() { + return ((StringValue*)_data.rc)->value(); + } + bool Value::is_void() const { return _type == VOID; } @@ -221,6 +241,18 @@ namespace basil { return *(MacroValue*)_data.rc; } + bool Value::is_runtime() const { + return _type->kind() == KIND_RUNTIME; + } + + const ASTNode* Value::get_runtime() const { + return (ASTNode*)_data.rc; + } + + ASTNode* Value::get_runtime() { + return (ASTNode*)_data.rc; + } + const Type* Value::type() const { return _type; } @@ -230,6 +262,7 @@ namespace basil { else if (is_error()) write(io, "error"); else if (is_int()) write(io, get_int()); else if (is_symbol()) write(io, symbol_for(get_symbol())); + else if (is_string()) write(io, '"', get_string(), '"'); else if (is_type()) write(io, get_type()); else if (is_bool()) write(io, get_bool()); else if (is_list()) { @@ -253,9 +286,11 @@ namespace basil { } write(io, ")"); } - else if (is_function()) write(io, ""); - else if (is_alias()) write(io, ""); - else if (is_macro()) write(io, ""); + else if (is_function()) write(io, "<#procedure>"); + else if (is_alias()) write(io, "<#alias>"); + else if (is_macro()) write(io, "<#macro>"); + else if (is_runtime()) + write(io, "<#runtime ", ((const RuntimeType*)_type)->base(), ">"); } u64 Value::hash() const { @@ -263,6 +298,7 @@ namespace basil { else if (is_error()) return 14933118315469276343ul; else if (is_int()) return ::hash(get_int()) ^ 6909969109598810741ul; else if (is_symbol()) return ::hash(get_symbol()) ^ 1899430078708870091ul; + else if (is_string()) return ::hash(get_string()) ^ 1276873522146073541ul; else if (is_type()) return get_type()->hash(); else if (is_bool()) return get_bool() ? 9269586835432337327ul @@ -307,22 +343,26 @@ namespace basil { } return h; } + else if (is_runtime()) { + return _type->hash() ^ ::hash(_data.rc); + } return 0; } bool Value::operator==(const Value& other) const { - if (type() != other.type()) return false; + if (type() != other.type()) return false; else if (is_int()) return get_int() == other.get_int(); else if (is_symbol()) return get_symbol() == other.get_symbol(); else if (is_type()) return get_type() == other.get_type(); else if (is_bool()) return get_bool() == other.get_bool(); + else if (is_string()) return get_string() == other.get_string(); else if (is_list()) { - const ListValue* l = &get_list(), *o = &other.get_list(); - do { - if (l->head() != o->head()) return false; - l = &l->tail().get_list(), o = &o->tail().get_list(); - } while (l->tail().is_list() && o->tail().is_list()); - return l->head() == o->head() && l->tail().is_void() && o->tail().is_void(); + const Value* l = this, *o = &other; + while (l->is_list() && o->is_list()) { + if (l->get_list().head() != o->get_list().head()) return false; + l = &l->get_list().tail(), o = &o->get_list().tail(); + } + return l->is_void() && o->is_void(); } else if (is_function()) { if (get_function().is_builtin()) @@ -352,6 +392,9 @@ namespace basil { return get_macro().body() == other.get_macro().body(); } } + else if (is_runtime()) { + return _data.rc == other._data.rc; + } return type() == other.type(); } @@ -359,6 +402,8 @@ namespace basil { if (is_list()) return Value(new ListValue(get_list().head().clone(), get_list().tail().clone())); + else if (is_string()) + return Value(get_string(), STRING); else if (is_sum()) return Value(new SumValue(get_sum().value()), type()); else if (is_product()) { @@ -388,6 +433,9 @@ namespace basil { get_macro().args(), get_macro().body().clone())); } } + else if (is_runtime()) { + // todo: ast cloning + } return *this; } @@ -403,6 +451,17 @@ namespace basil { return _loc; } + StringValue::StringValue(const string& value): + _value(value) {} + + string& StringValue::value() { + return _value; + } + + const string& StringValue::value() const { + return _value; + } + ListValue::ListValue(const Value& head, const Value& tail) : _head(head), _tail(tail) {} @@ -464,13 +523,55 @@ namespace basil { return _values.end(); } + const u64 KEYWORD_ARG_BIT = 1ul << 63; + const u64 ARG_NAME_MASK = ~KEYWORD_ARG_BIT; + FunctionValue::FunctionValue(ref env, const vector& args, - const Value& code): - _code(code), _builtin(nullptr), _env(env), _args(args) {} + const Value& code, i64 name): + _name(name), _code(code), _builtin(nullptr), + _env(env), _args(args), _insts(nullptr), _calls(nullptr) {} FunctionValue::FunctionValue(ref env, BuiltinFn builtin, - u64 arity): - _builtin(builtin), _env(env), _builtin_arity(arity) {} + u64 arity, i64 name): + _name(name), _builtin(builtin), _env(env), + _builtin_arity(arity), _insts(nullptr), _calls(nullptr) {} + + FunctionValue::~FunctionValue() { + if (_insts) { + for (auto& p : *_insts) p.second->dec(); + delete _insts; + } + } + + FunctionValue::FunctionValue(const FunctionValue& other): + _name(other._name), _code(other._code.clone()), + _builtin(other._builtin), _env(other._env), _args(other._args), + _insts(other._insts ? + new map(*other._insts) + : nullptr), + _calls(other._calls ? + new set(*other._calls) : nullptr) { + if (_insts) for (auto& p : *_insts) p.second->inc(); + } + + FunctionValue& FunctionValue::operator=(const FunctionValue& other) { + if (this != &other) { + if (_insts) { + for (auto& p : *_insts) p.second->dec(); + delete _insts; + } + _name = other._name; + _code = other._code.clone(); + _builtin = other._builtin; + _env = other._env; + _args = other._args; + _insts = other._insts ? + new map(*other._insts) + : nullptr; + if (_insts) for (auto& p : *_insts) p.second->inc(); + } + return *this; + } const vector& FunctionValue::args() const { return _args; @@ -492,6 +593,49 @@ namespace basil { return _env; } + i64 FunctionValue::name() const { + return _name; + } + + bool FunctionValue::found_calls() const { + return _calls; + } + + bool FunctionValue::recursive() const { + return _calls && _calls->find(this) != _calls->end(); + } + + void FunctionValue::add_call(const FunctionValue* other) { + if (!_calls) _calls = new set(); + if (other->_calls) for (const FunctionValue* f : *other->_calls) { + _calls->insert(f); + ((FunctionValue*)f)->inc(); // evil + } + _calls->insert(other); + ((FunctionValue*)other)->inc(); + } + + ASTNode* FunctionValue::instantiation(const Type* type) const { + if (_insts) { + auto it = _insts->find(type); + if (it != _insts->end()) return it->second; + } + return nullptr; + } + + void FunctionValue::instantiate(const Type* type, ASTNode* body) { + if (!_insts) _insts = new map(); + auto it = _insts->find(type); + if (it == _insts->end()) { + _insts->put(type, body); + } + else { + it->second->dec(); + it->second = body; + } + body->inc(); + } + u64 FunctionValue::arity() const { return _builtin ? _builtin_arity : _args.size(); } @@ -546,8 +690,47 @@ namespace basil { const Value& MacroValue::body() const { return _code; } - - Value binary_arithmetic(const Value& lhs, const Value& rhs, i64(*op)(i64, i64)) { + + vector to_vector(const Value& list) { + vector values; + const Value* v = &list; + while (v->is_list()) { + values.push(v->get_list().head()); + v = &v->get_list().tail(); + } + return values; + } + + Value lower(const Value& v) { + if (v.is_runtime()) return v; + else if (v.is_void()) return new ASTVoid(v.loc()); + else if (v.is_int()) + return new ASTInt(v.loc(), v.get_int()); + else if (v.is_symbol()) + return new ASTSymbol(v.loc(), v.get_symbol()); + else if (v.is_string()) + return new ASTString(v.loc(), v.get_string()); + else if (v.is_bool()) + return new ASTBool(v.loc(), v.get_bool()); + else if (v.is_list()) { + vector vals = to_vector(v); + ASTNode* acc = new ASTVoid(v.loc()); + for (i64 i = vals.size() - 1; i >= 0; i --) { + Value l = lower(vals[i]); + acc = new ASTCons(v.loc(), l.get_runtime(), acc); + } + return acc; + } + else if (v.is_error()) + return new ASTSingleton(ERROR); + else { + err(v.loc(), "Couldn't lower value '", v, "'."); + return error(); + } + } + + Value binary_arithmetic(const Value& lhs, const Value& rhs, + i64(*op)(i64, i64)) { if (!lhs.is_int() && !lhs.is_error()) { err(lhs.loc(), "Expected integer value in arithmetic expression, found '", lhs.type(), "'."); @@ -562,24 +745,38 @@ namespace basil { return Value(op(lhs.get_int(), rhs.get_int())); } + + bool is_runtime_binary(const Value& lhs, const Value& rhs) { + return lhs.is_runtime() || rhs.is_runtime(); + } + + Value lower(ASTMathOp op, const Value& lhs, const Value& rhs) { + return new ASTBinaryMath(lhs.loc(), op, lower(lhs).get_runtime(), + lower(rhs).get_runtime()); + } Value add(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_ADD, lhs, rhs); return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a + b; }); } Value sub(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_SUB, lhs, rhs); return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a - b; }); } Value mul(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_MUL, lhs, rhs); return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a * b; }); } Value div(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_DIV, lhs, rhs); return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a / b; }); } Value rem(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_REM, lhs, rhs); return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a % b; }); } @@ -599,19 +796,29 @@ namespace basil { return Value(op(lhs.get_bool(), rhs.get_bool()), BOOL); } + Value lower(ASTLogicOp op, const Value& lhs, const Value& rhs) { + return new ASTBinaryLogic(lhs.loc(), op, lower(lhs).get_runtime(), + lower(rhs).get_runtime()); + } + Value logical_and(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_AND, lhs, rhs); return binary_logic(lhs, rhs, [](bool a, bool b) -> bool { return a && b; }); } Value logical_or(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_OR, lhs, rhs); return binary_logic(lhs, rhs, [](bool a, bool b) -> bool { return a || b; }); } Value logical_xor(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_XOR, lhs, rhs); return binary_logic(lhs, rhs, [](bool a, bool b) -> bool { return a ^ b; }); } Value logical_not(const Value& v) { + if (v.is_runtime()) return new ASTNot(v.loc(), lower(v).get_runtime()); + if (!v.is_bool() && !v.is_error()) { err(v.loc(), "Expected boolean value in logical expression, found '", v.type(), "'."); @@ -622,48 +829,71 @@ namespace basil { return Value(!v.get_bool(), BOOL); } + Value lower(ASTEqualOp op, const Value& lhs, const Value& rhs) { + return new ASTBinaryEqual(lhs.loc(), op, lower(lhs).get_runtime(), + lower(rhs).get_runtime()); + } + Value equal(const Value& lhs, const Value& rhs) { if (lhs.is_error() || rhs.is_error()) return error(); + if (is_runtime_binary(lhs, rhs)) return lower(AST_EQUAL, lhs, rhs); return Value(lhs == rhs, BOOL); } Value inequal(const Value& lhs, const Value& rhs) { + if (lhs.is_error() || rhs.is_error()) return error(); + if (is_runtime_binary(lhs, rhs)) return lower(AST_INEQUAL, lhs, rhs); return Value(!equal(lhs, rhs).get_bool(), BOOL); } - Value binary_relation(const Value& lhs, const Value& rhs, bool(*op)(i64, i64)) { - if (!lhs.is_int() && !lhs.is_error()) { - err(lhs.loc(), "Expected integer value in relational expression, found '", + Value binary_relation(const Value& lhs, const Value& rhs, bool(*int_op)(i64, i64), bool(*string_op)(string, string)) { + if (!lhs.is_int() && !lhs.is_string() && !lhs.is_error()) { + err(lhs.loc(), "Expected integer or string value in relational expression, found '", lhs.type(), "'."); return error(); } - if (!rhs.is_int() && !lhs.is_error()) { - err(rhs.loc(), "Expected integer value in relational expression, found '", + if (!rhs.is_int() && !rhs.is_string() && !lhs.is_error()) { + err(rhs.loc(), "Expected integer or string value in relational expression, found '", rhs.type(), "'."); return error(); } + if ((lhs.is_int() && !rhs.is_int()) || (lhs.is_string() && !rhs.is_string())) { + err(rhs.loc(), "Invalid parameters to relational expression: '", lhs.type(), "' and '", rhs.type(), "'."); + return error(); + } if (lhs.is_error() || rhs.is_error()) return error(); - return Value(op(lhs.get_int(), rhs.get_int()), BOOL); + if (lhs.is_string()) return Value(string_op(lhs.get_string(), rhs.get_string()), BOOL); + return Value(int_op(lhs.get_int(), rhs.get_int()), BOOL); } + Value lower(ASTRelOp op, const Value& lhs, const Value& rhs) { + return new ASTBinaryRel(lhs.loc(), op, lower(lhs).get_runtime(), + lower(rhs).get_runtime()); + } + Value less(const Value& lhs, const Value& rhs) { - return binary_relation(lhs, rhs, [](i64 a, i64 b) -> bool { return a < b; }); + if (is_runtime_binary(lhs, rhs)) return lower(AST_LESS, lhs, rhs); + return binary_relation(lhs, rhs, [](i64 a, i64 b) -> bool { return a < b; }, [](string a, string b) -> bool { return a < b; }); } Value greater(const Value& lhs, const Value& rhs) { - return binary_relation(lhs, rhs, [](i64 a, i64 b) -> bool { return a > b; }); + if (is_runtime_binary(lhs, rhs)) return lower(AST_GREATER, lhs, rhs); + return binary_relation(lhs, rhs, [](i64 a, i64 b) -> bool { return a > b; }, [](string a, string b) -> bool { return a > b; }); } Value less_equal(const Value& lhs, const Value& rhs) { - return binary_relation(lhs, rhs, [](i64 a, i64 b) -> bool { return a <= b; }); + if (is_runtime_binary(lhs, rhs)) return lower(AST_LESS_EQUAL, lhs, rhs); + return binary_relation(lhs, rhs, [](i64 a, i64 b) -> bool { return a <= b; }, [](string a, string b) -> bool { return a <= b; }); } Value greater_equal(const Value& lhs, const Value& rhs) { - return binary_relation(lhs, rhs, [](i64 a, i64 b) -> bool { return a >= b; }); + if (is_runtime_binary(lhs, rhs)) return lower(AST_GREATER_EQUAL, lhs, rhs); + return binary_relation(lhs, rhs, [](i64 a, i64 b) -> bool { return a >= b; }, [](string a, string b) -> bool { return a >= b; }); } Value head(const Value& v) { + if (v.is_runtime()) return new ASTHead(v.loc(), lower(v).get_runtime()); if (!v.is_list() && !v.is_error()) { err(v.loc(), "Can only get head of value of list type, given '", v.type(), "'."); @@ -675,6 +905,7 @@ namespace basil { } Value tail(const Value& v) { + if (v.is_runtime()) return new ASTTail(v.loc(), lower(v).get_runtime()); if (!v.is_list() && !v.is_error()) { err(v.loc(), "Can only get tail of value of list type, given '", v.type(), "'."); @@ -686,6 +917,10 @@ namespace basil { } Value cons(const Value& head, const Value& tail) { + if (head.is_runtime() || tail.is_runtime()) { + return new ASTCons(head.loc(), lower(head).get_runtime(), + lower(tail).get_runtime()); + } if (!tail.is_list() && !tail.is_void() && !tail.is_error()) { err(tail.loc(), "Tail of cons cell must be a list or void, given '", tail.type(), "'."); @@ -713,15 +948,78 @@ namespace basil { return l; } + Value is_empty(const Value& list) { + if (list.is_runtime()) + return new ASTIsEmpty(list.loc(), lower(list).get_runtime()); + if (!list.is_list() && !list.is_void() && !list.is_error()) { + err(list.loc(), "Can only get tail of value of list type, given '", + list.type(), "'."); + return error(); + } + return Value(list.is_void(), BOOL); + } + Value error() { return Value(ERROR); } + Value read_line() { + return new ASTReadLine(NO_LOCATION); + } + Value type_of(const Value& v) { return Value(v.type(), TYPE); } - Value call(const Value& function, const Value& arg) { + ASTNode* instantiate(SourceLocation loc, FunctionValue& fn, + const Type* args_type) { + ref new_env = fn.get_env()->clone(); + new_env->make_runtime(); + u32 j = 0; + vector new_args; + for (u32 i = 0; i < fn.arity(); i ++) { + if (!(fn.args()[i] & KEYWORD_ARG_BIT)) { + auto it = new_env->find(symbol_for(fn.args()[i] & ARG_NAME_MASK)); + const Type* argt = ((const ProductType*)args_type)->member(j); + it->value = new ASTSingleton(argt); + j ++; + new_args.push(fn.args()[i]); + } + } + Value v = eval(new_env, fn.body().clone()); + if (v.is_error()) return nullptr; + if (!v.is_runtime()) v = lower(v); + ASTNode* result = new ASTFunction(loc, new_env, args_type, + new_args, v.get_runtime(), fn.name()); + fn.instantiate(args_type, result); + return result; + } + + void find_calls(FunctionValue& fn, ref env, const Value& term, + set& visited) { + if (!term.is_list()) return; + Value h = head(term); + if (h.is_symbol()) { + Def* def = env->find(symbol_for(h.get_symbol())); + if (def && def->value.is_function()) { + FunctionValue* f = &def->value.get_function(); + if (visited.find(f) == visited.end()) { + visited.insert(f); + if (f != &fn) find_calls(*f, f->get_env(), f->body(), visited); + fn.add_call(f); + } + } + } + if (!introduces_env(term)) { + const Value* v = &term; + while (v->is_list()) { + find_calls(fn, env, v->get_list().head(), visited); + v = &v->get_list().tail(); + } + } + } + + Value call(ref env, Value& function, const Value& arg) { if (!function.is_function() && !function.is_error()) { err(function.loc(), "Called value is not a procedure."); return error(); @@ -732,9 +1030,9 @@ namespace basil { } if (function.is_error() || arg.is_error()) return error(); - const FunctionValue& fn = function.get_function(); + FunctionValue& fn = function.get_function(); if (fn.is_builtin()) { - return fn.get_builtin()(fn.get_env(), arg); + return fn.get_builtin()(env, arg); } else { ref env = fn.get_env(); @@ -744,13 +1042,96 @@ namespace basil { argc, " provided."); return error(); } + + bool runtime_call = false; + for (u32 i = 0; i < argc; i ++) { + if (arg.get_product()[i].is_runtime()) runtime_call = true; + } + if (!fn.found_calls()) { + set visited; + find_calls(fn, env, fn.body(), visited); + } + if (fn.recursive()) runtime_call = true; + + if (runtime_call) { + vector argts; + vector lowered_args; + for (u32 i = 0; i < argc; i ++) { + if (fn.args()[i] & KEYWORD_ARG_BIT) { + // keyword arg + if (!arg.get_product()[i].is_symbol() || + arg.get_product()[i].get_symbol() != + (fn.args()[i] & ARG_NAME_MASK)) { + err(arg.get_product()[i].loc(), "Expected keyword '", + symbol_for(fn.args()[i] & ARG_NAME_MASK), "'."); + return error(); + } + } + else { + Value lowered = lower(arg.get_product()[i]); + argts.push(((const RuntimeType*)lowered.type())->base()); + lowered_args.push(lowered); + } + } + const Type* argt = find(argts); + ASTNode* body = fn.instantiation(argt); + if (!body) { + fn.instantiate(argt, new ASTIncompleteFn(function.loc(), argt, fn.name())); + body = instantiate(function.loc(), fn, argt); + } + if (!body) return error(); + vector arg_nodes; + for (Value& v : lowered_args) arg_nodes.push(v.get_runtime()); + return new ASTCall(function.loc(), body, arg_nodes); + } + for (u32 i = 0; i < arity; i ++) { - const string& argname = symbol_for(fn.args()[i]); - env->find(argname)->value = arg.get_product()[i]; + if (fn.args()[i] & KEYWORD_ARG_BIT) { + // keyword arg + if (!arg.get_product()[i].is_symbol() || + arg.get_product()[i].get_symbol() != + (fn.args()[i] & ARG_NAME_MASK)) { + err(arg.get_product()[i].loc(), "Expected keyword '", + symbol_for(fn.args()[i] & ARG_NAME_MASK), "'."); + return error(); + } + } + else { + const string& argname = symbol_for(fn.args()[i] & ARG_NAME_MASK); + env->find(argname)->value = arg.get_product()[i]; + } } return eval(env, fn.body()); } } + + Value display(const Value& arg) { + return new ASTDisplay(arg.loc(), lower(arg).get_runtime()); + } + + Value assign(ref env, const Value &dest, const Value& src) { + if (!dest.is_symbol()) { + err(dest.loc(), "Invalid destination in assignment '", dest, "'."); + return error(); + } + + Def* def = env->find(symbol_for(dest.get_symbol())); + if (!def) { + err(dest.loc(), "Undefined variable '", + symbol_for(dest.get_symbol()), "'."); + return error(); + } + Value lowered = src; + if (!lowered.is_runtime()) lowered = lower(src); + if (def->value.is_runtime()) + return new ASTAssign(dest.loc(), env, + dest.get_symbol(), lowered.get_runtime()); + else { + def->value = lower(def->value); + return new ASTDefine(dest.loc(), env, + dest.get_symbol(), lowered.get_runtime()); + } + } } template<> diff --git a/values.h b/values.h index f977487..7486bc1 100644 --- a/values.h +++ b/values.h @@ -1,15 +1,19 @@ #ifndef BASIL_VALUES_H #define BASIL_VALUES_H -#include "defs.h" +#include "util/defs.h" #include "type.h" -#include "io.h" -#include "hash.h" +#include "util/io.h" +#include "util/hash.h" #include "errors.h" -#include "vec.h" -#include "rc.h" +#include "util/vec.h" +#include "util/rc.h" namespace basil { + class Def; + class Env; + class ASTNode; + u64 symbol_value(const string& symbol); const string& symbol_for(u64 value); @@ -42,6 +46,7 @@ namespace basil { Value(FunctionValue* f); Value(AliasValue* f); Value(MacroValue* f); + Value(ASTNode* n); ~Value(); Value(const Value& other); Value& operator=(const Value& other); @@ -54,6 +59,10 @@ namespace basil { u64 get_symbol() const; u64& get_symbol(); + bool is_string() const; + const string& get_string() const; + string& get_string(); + bool is_void() const; bool is_error() const; @@ -90,6 +99,10 @@ namespace basil { const MacroValue& get_macro() const; MacroValue& get_macro(); + bool is_runtime() const; + const ASTNode* get_runtime() const; + ASTNode* get_runtime(); + const Type* type() const; void format(stream& io) const; u64 hash() const; @@ -101,6 +114,15 @@ namespace basil { SourceLocation loc() const; }; + class StringValue : public RC { + string _value; + public: + StringValue(const string& value); + + string& value(); + const string& value() const; + }; + class ListValue : public RC { Value _head, _tail; public: @@ -137,16 +159,26 @@ namespace basil { using BuiltinFn = Value (*)(ref, const Value& params); + extern const u64 KEYWORD_ARG_BIT; + extern const u64 ARG_NAME_MASK; + class FunctionValue : public RC { + i64 _name; Value _code; BuiltinFn _builtin; ref _env; u64 _builtin_arity; vector _args; + set* _calls; + map* _insts; public: FunctionValue(ref env, const vector& args, - const Value& code); - FunctionValue(ref env, BuiltinFn builtin, u64 arity); + const Value& code, i64 name = -1); + FunctionValue(ref env, BuiltinFn builtin, u64 arity, + i64 name = -1); + ~FunctionValue(); + FunctionValue(const FunctionValue& other); + FunctionValue& operator=(const FunctionValue& other); const vector& args() const; const Value& body() const; @@ -155,6 +187,12 @@ namespace basil { BuiltinFn get_builtin() const; ref get_env(); const ref get_env() const; + i64 name() const; + bool found_calls() const; + bool recursive() const; + void add_call(const FunctionValue* other); + ASTNode* instantiation(const Type* args) const; + void instantiate(const Type* args, ASTNode* body); }; class AliasValue : public RC { @@ -188,6 +226,8 @@ namespace basil { const ref get_env() const; }; + Value lower(const Value& v); + Value add(const Value& lhs, const Value& rhs); Value sub(const Value& lhs, const Value& rhs); Value mul(const Value& lhs, const Value& rhs); @@ -211,6 +251,8 @@ namespace basil { Value cons(const Value& head, const Value& tail); Value empty(); Value list_of(const Value& element); + vector to_vector(const Value& list); + Value is_empty(const Value& list); template Value list_of(const Value& element, Args... args) { @@ -220,9 +262,16 @@ namespace basil { Value list_of(const vector& elements); Value error(); + + Value read_line(); + Value type_of(const Value& v); - Value call(const Value& function, const Value& arg); + Value call(ref env, Value& function, const Value& arg); + + Value display(const Value& arg); + + Value assign(ref env, const Value& dest, const Value& src); } template<> From 447eca09cbde00117e96bd721a93eb6f2b3d98c6 Mon Sep 17 00:00:00 2001 From: elucent <9532786+elucent@users.noreply.github.com> Date: Tue, 1 Sep 2020 00:30:43 -0400 Subject: [PATCH 04/17] Added first-class functions at runtime. --- .gitignore | 3 +- Makefile | 4 +- README.md | 5 ++ ast.cpp | 164 ++++++++++++++++++++++++++++--------------- ast.h | 35 ++++----- eval.cpp | 106 ++++++++++++++++++++++------ example/factorial.bl | 3 + example/fibonacci.bl | 5 ++ example/guessing.bl | 17 +++++ example/list-ops.bl | 37 ++++++++++ main.cpp | 18 +++-- native.cpp | 41 +++++++++++ ssa.cpp | 15 ++-- ssa.h | 4 +- std/list.bl | 24 ++++++- std/math.bl | 10 +++ tests/example.bl | 6 -- type.cpp | 84 +++++++++++++++++++--- type.h | 10 ++- values.cpp | 141 ++++++++++++++++++++++++++++++++++--- values.h | 3 + 21 files changed, 599 insertions(+), 136 deletions(-) create mode 100644 example/factorial.bl create mode 100644 example/fibonacci.bl create mode 100644 example/guessing.bl create mode 100644 example/list-ops.bl create mode 100644 std/math.bl delete mode 100644 tests/example.bl diff --git a/.gitignore b/.gitignore index 681d0a0..bd48028 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.o -basil \ No newline at end of file +basil +.vscode/ diff --git a/Makefile b/Makefile index 3d5ed57..8e7a87b 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ OBJS := $(patsubst %.cpp,%.o,$(SRCS)) CXX := clang++ CXXHEADERS := -I. -Iutil -Ijasmine -CXXFLAGS := $(CXXHEADERS) -std=c++17 -ffast-math -fno-rtti -fno-exceptions -ffunction-sections -Wno-null-dereference +CXXFLAGS := $(CXXHEADERS) -std=c++17 -ffast-math -fno-rtti -fno-exceptions -Wno-null-dereference clean: rm -f $(OBJS) *.o.tmp basil @@ -19,4 +19,4 @@ release: $(OBJS) $(CXX) $(CXXFLAGS) $^ -o basil %.o: %.cpp %.h - $(CXX) $(CXXFLAGS) -c $< -o $@ + $(CXX) $(CXXFLAGS) -c $< -o $@ \ No newline at end of file diff --git a/README.md b/README.md index 9e22032..b31a5b5 100644 --- a/README.md +++ b/README.md @@ -490,6 +490,11 @@ specified in terms of the types they accept and produce. | `?`³ | `Bool * 'T0 * 'T0 -> 'T0` | Ternary conditional operator. | | `=` | `Symbol * 'T0 -> Void` | Assigns variable to value. | | `display` | `'T0 -> Void` | Prints a value to standard output on its own line. | +| `read-line` | `() -> String` | Reads a line from standard input. | +| `read-word` | `() -> String` | Reads a space-delimited string from standard input. | +| `read-int` | `() -> Int` | Reads an integer from standard input. | +| `length` | `String | 'T0 List -> Int` | Returns the length of a string or list. | +| 1. Value equality for integers, bools, strings, and symbols; reference equality for lists. diff --git a/ast.cpp b/ast.cpp index 01fc509..777fd7d 100644 --- a/ast.cpp +++ b/ast.cpp @@ -15,10 +15,7 @@ namespace basil { const Type* ASTNode::type() { if (!_type) _type = lazy_type(); - if (_type->kind() == KIND_TYPEVAR - && ((const TypeVariable*)_type)->actual() != ANY) - return ((const TypeVariable*)_type)->actual(); // unwrap concrete typevars - return _type; + return _type->concretify(); } ASTSingleton::ASTSingleton(const Type* type): @@ -245,8 +242,8 @@ namespace basil { return BOOL; } - Location ASTNot::emit(Function& function) { - return function.add(new NotInsn(_child->emit(function))); + Location ASTNot::emit(Function& func) { + return func.add(new NotInsn(_child->emit(func))); } void ASTNot::format(stream& io) const { @@ -266,7 +263,10 @@ namespace basil { if (_left->type() == STRING || _right->type() == STRING) { func.add(new StoreArgumentInsn(_left->emit(func), 0, _left->type())); func.add(new StoreArgumentInsn(_right->emit(func), 1, _right->type())); - Location result = func.add(new CallInsn(ssa_find_label("_strcmp"), INT)); + Location label; + label.type = SSA_LABEL; + label.label_index = ssa_find_label("_strcmp"); + Location result = func.add(new CallInsn(label, INT)); return func.add(new EqualInsn(result, ssa_immediate(0))); } @@ -315,7 +315,10 @@ namespace basil { if (_left->type() == STRING || _right->type() == STRING) { func.add(new StoreArgumentInsn(_left->emit(func), 0, _left->type())); func.add(new StoreArgumentInsn(_right->emit(func), 1, _right->type())); - Location result = func.add(new CallInsn(ssa_find_label("_strcmp"), INT)); + Location label; + label.type = SSA_LABEL; + label.label_index = ssa_find_label("_strcmp"); + Location result = func.add(new CallInsn(label, INT)); switch(_op) { case AST_LESS: @@ -386,12 +389,16 @@ namespace basil { const Type* fntype = _func->type(); const Type* argt = ((const FunctionType*)fntype)->arg(); if (fntype == ERROR || argt == ERROR) return ERROR; + vector argts; for (u32 i = 0; i < _args.size(); i ++) { if (_args[i]->type() == ERROR) return ERROR; - if (!unify(_args[i]->type(), ((const ProductType*)argt)->member(i))) { - err(_args[i]->loc(), "Invalid argument ", i, " to function call."); - return ERROR; - } + argts.push(_args[i]->type()); + } + const Type* provided_argt = find(argts); + // println("calling ", _func, argt, " on ", provided_argt); + if (!unify(argt, provided_argt)) { + err(loc(), "Invalid arguments ", provided_argt, " to ", _func, "."); + return ERROR; } return ((const FunctionType*)fntype)->ret(); } @@ -404,10 +411,14 @@ namespace basil { arglocs.push(_args[i]->emit(func)); } for (u32 i = 0; i < _args.size(); i ++) { + if (arglocs[i].type == SSA_LABEL) { + arglocs[i] = func.add(new AddressInsn(arglocs[i], + ((const ProductType*)argt)->member(i))); + } func.add(new StoreArgumentInsn(arglocs[i], i, ((const ProductType*)argt)->member(i))); } - return func.add(new CallInsn(fn.label_index, type())); + return func.add(new CallInsn(fn, type())); } void ASTCall::format(stream& io) const { @@ -606,8 +617,8 @@ namespace basil { return BOOL; } - Location ASTIsEmpty::emit(Function& function) { - return function.add(new EqualInsn(_child->emit(function), ssa_immediate(0))); + Location ASTIsEmpty::emit(Function& func) { + return func.add(new EqualInsn(_child->emit(func), ssa_immediate(0))); } void ASTIsEmpty::format(stream& io) const { @@ -630,8 +641,8 @@ namespace basil { return ((const ListType*) _child->type())->element(); } - Location ASTHead::emit(Function& function) { - return function.add(new LoadPtrInsn(_child->emit(function), type(), 0)); + Location ASTHead::emit(Function& func) { + return func.add(new LoadPtrInsn(_child->emit(func), type(), 0)); } void ASTHead::format(stream& io) const { @@ -654,8 +665,8 @@ namespace basil { return _child->type(); } - Location ASTTail::emit(Function& function) { - return function.add(new LoadPtrInsn(_child->emit(function), type(), 8)); + Location ASTTail::emit(Function& func) { + return func.add(new LoadPtrInsn(_child->emit(func), type(), 8)); } void ASTTail::format(stream& io) const { @@ -686,17 +697,52 @@ namespace basil { } } - Location ASTCons::emit(Function& function) { - Location l = _left->emit(function), r = _right->emit(function); - function.add(new StoreArgumentInsn(l, 0, _left->type())); - function.add(new StoreArgumentInsn(r, 1, _right->type())); - return function.add(new CallInsn(ssa_find_label("_cons"), type())); + Location ASTCons::emit(Function& func) { + Location l = _left->emit(func), r = _right->emit(func); + func.add(new StoreArgumentInsn(l, 0, _left->type())); + func.add(new StoreArgumentInsn(r, 1, _right->type())); + Location label; + label.type = SSA_LABEL; + label.label_index = ssa_find_label("_cons"); + return func.add(new CallInsn(label, type())); } void ASTCons::format(stream& io) const { write(io, "(cons ", _left, " ", _right, ")"); } + const Type* ASTLength::lazy_type() { + const Type *child = _child->type(); + if (child == ERROR) return ERROR; + if (unify(_child->type(), STRING) != STRING + && unify(_child->type(), find(find()))->kind() + != KIND_LIST) { + err(_child->loc(), "Argument to 'length' expression must be string or list, ", + "given '", _child->type()); + return ERROR; + } + return INT; + } + + ASTLength::ASTLength(SourceLocation loc, ASTNode* child) + : basil::ASTUnary(loc, child) {} + + Location ASTLength::emit(Function& func) { + func.add(new StoreArgumentInsn(_child->emit(func), 0, _child->type())); + + Location label; + label.type = SSA_LABEL; + if (_child->type() == STRING) label.label_index = ssa_find_label("_strlen"); + else label.label_index = ssa_find_label("_listlen"); + + return func.add(new CallInsn(label, INT)); + } + + void ASTLength::format(stream& io) const { + write(io, "(length ", _child, ")"); + } + + const Type* ASTDisplay::lazy_type() { return VOID; } @@ -704,7 +750,7 @@ namespace basil { ASTDisplay::ASTDisplay(SourceLocation loc, ASTNode* node): ASTUnary(loc, node) {} - Location ASTDisplay::emit(Function& function) { + Location ASTDisplay::emit(Function& func) { const char* name; if (_child->type() == INT) name = "_display_int"; else if (_child->type() == SYMBOL) name = "_display_symbol"; @@ -715,50 +761,52 @@ namespace basil { else if (_child->type() == find(BOOL)) name = "_display_bool_list"; else if (_child->type() == find(STRING)) name = "_display_string_list"; else if (_child->type() == VOID) name = "_display_int_list"; - function.add(new StoreArgumentInsn(_child->emit(function), 0, _child->type())); - return function.add(new CallInsn(ssa_find_label(name), type())); + func.add(new StoreArgumentInsn(_child->emit(func), 0, _child->type())); + Location label; + label.type = SSA_LABEL; + label.label_index = ssa_find_label(name); + return func.add(new CallInsn(label, type())); } void ASTDisplay::format(stream& io) const { write(io, "(display ", _child, ")"); } - const Type* ASTReadLine::lazy_type() { - return STRING; - } - - ASTReadLine::ASTReadLine(SourceLocation loc) : ASTNode(loc) {} - - Location ASTReadLine::emit(Function& function) { - return function.add(new CallInsn(ssa_find_label("_read_line"), STRING)); - } - - void ASTReadLine::format(stream& io) const { - write(io, "(read-line)"); - } - - // const Type* ASTNativeCall::lazy_type() { + ASTNativeCall::ASTNativeCall(SourceLocation loc, const string &func_name, const Type* ret) + : basil::ASTNode(loc), _func_name(func_name), _ret(ret) {} - // } + ASTNativeCall::ASTNativeCall(SourceLocation loc, const string &func_name, const Type *ret, const vector& args, const vector& arg_types) + : ASTNode(loc), _func_name(func_name), _ret(ret), _args(args), _arg_types(arg_types) { + for (ASTNode* node : _args) node->inc(); + } - // ASTNativeCall::ASTNativeCall(SourceLocation loc, const string &func, vector args) - // : ASTNode(loc), _func(func), _args(args) {} + ASTNativeCall::~ASTNativeCall() { + for (ASTNode* node : _args) node->dec(); + } - // Location ASTNativeCall::emit(Function& function) { - // for (int i = 0; i < _args.size(); i ++) - // function.add(new StoreArgumentInsn(_args[i]->emit(function), i, _args[i]->type())); - // // return function.add(new CallInsn(ssa_find_label(_func), )); - // } + const Type* ASTNativeCall::lazy_type() { + for (int i = 0; i < _args.size(); i ++) { + if (!unify(_args[i]->type(), _arg_types[i])) { + err(_args[i]->loc(), "Expected '", _arg_types[i], "', given '", _args[i]->type(), "'."); + } + } + return _ret; + } - // void ASTNativeCall::format(stream& io) const { - // write(io, "(", _func, _args.size() ? " " : ""); - // for (const ASTNode *arg : _args) { - // write(io, " ", arg); - // } - // write(io, ")"); - // } - + Location ASTNativeCall::emit(Function& func) { + for (int i = 0; i < _args.size(); i ++) + func.add(new StoreArgumentInsn(_args[i]->emit(func), i, _args[i]->type())); + Location label; + label.type = SSA_LABEL; + label.label_index = ssa_find_label(_func_name); + return func.add(new CallInsn(label, _ret)); + } + void ASTNativeCall::format(stream& io) const { + write(io, "(", _func_name); + for (const ASTNode *arg : _args) write(io, " ", arg); + write(io, ")"); + } ASTAssign::ASTAssign(SourceLocation loc, const ref env, u64 dest, ASTNode* src): ASTUnary(loc, src), _env(env), _dest(dest) {} diff --git a/ast.h b/ast.h index 698655e..cc2b88f 100644 --- a/ast.h +++ b/ast.h @@ -339,6 +339,16 @@ namespace basil { void format(stream& io) const override; }; + class ASTLength : public ASTUnary { + protected: + const Type* lazy_type() override; + public: + ASTLength(SourceLocation loc, ASTNode* child); + + Location emit(Function& function) override; + void format(stream& io) const override; + }; + class ASTDisplay : public ASTUnary { protected: const Type* lazy_type() override; @@ -349,29 +359,22 @@ namespace basil { void format(stream& io) const override; }; - class ASTReadLine : public ASTNode { + class ASTNativeCall : public ASTNode { + const string _func_name; + const Type * _ret; + const vector _args; + const vector _arg_types; protected: const Type* lazy_type() override; public: - ASTReadLine(SourceLocation loc); - + ASTNativeCall(SourceLocation loc, const string &func_name, const Type* ret); + // TODO make args a variadic template instead of a vector + ASTNativeCall(SourceLocation loc, const string &func_name, const Type *ret, const vector& args, const vector& arg_types); + ~ASTNativeCall(); Location emit(Function& function) override; void format(stream& io) const override; }; - // class ASTNativeCall : public ASTNode { - // const string _func_name; - // const Type * _ret; - // vector _args; - // protected: - // const Type* lazy_type() override; - // public: - // ASTNativeCall(SourceLocation loc, const string &func_name, vector args); - - // Location emit(Function& function) override; - // void format(stream& io) const override; - // }; - class ASTAssign : public ASTUnary { ref _env; u64 _dest; diff --git a/eval.cpp b/eval.cpp index e23680d..abedaca 100644 --- a/eval.cpp +++ b/eval.cpp @@ -98,7 +98,23 @@ namespace basil { } Value builtin_read_line(ref env, const Value& args) { - return read_line(); + return new ASTNativeCall(NO_LOCATION, "_read_line", STRING); + } + + Value builtin_read_word(ref env, const Value& args) { + return new ASTNativeCall(NO_LOCATION, "_read_word", STRING); + } + + Value builtin_read_int(ref env, const Value& args) { + return new ASTNativeCall(NO_LOCATION, "_read_int", INT); + } + + Value builtin_length(ref env, const Value& args) { + return length(args.get_product()[0]); + } + + Value builtin_char_at(ref env, const Value& args) { + return char_at(args.get_product()[0], args.get_product()[1]); } Value builtin_if_macro(ref env, const Value& args) { @@ -160,6 +176,10 @@ namespace basil { root->infix_macro("?", new MacroValue(root, builtin_if_macro, 3), 3, 2); root->infix("#?", new FunctionValue(root, builtin_if, 3), 3, 2); root->def("read-line", new FunctionValue(root, builtin_read_line, 0), 0); + root->def("read-word", new FunctionValue(root, builtin_read_word, 0), 0); + root->def("read-int", new FunctionValue(root, builtin_read_int, 0), 0); + root->infix("length", new FunctionValue(root, builtin_length, 1), 1, 50); + root->infix("at", new FunctionValue(root, builtin_char_at, 2), 2, 90); root->def("true", Value(true, BOOL)); root->def("false", Value(false, BOOL)); return root; @@ -861,22 +881,23 @@ namespace basil { } if_true = cons(Value("do"), list_of(if_true_vals)); if (!params.is_list()) { - err(term.loc(), "If expression requires at least one else."); - return error(); + if_false = list_of(Value("list-of")); } - if (get_keyword(head(params)) == symbol_value("elif")) { + else if (get_keyword(head(params)) == symbol_value("elif")) { if_false = if_expr(env, params); return list_of(Value("#?"), cond, list_of(Value("quote"), if_true), list_of(Value("quote"), if_false)); } - params = tail(params); - while (params.is_list()) { - if_false_vals.push(head(params)); + else { params = tail(params); + while (params.is_list()) { + if_false_vals.push(head(params)); + params = tail(params); + } + if_false = cons(Value("do"), list_of(if_false_vals)); } - if_false = cons(Value("do"), list_of(if_false_vals)); return list_of(Value("#?"), cond, list_of(Value("quote"), if_true), @@ -958,6 +979,13 @@ namespace basil { return list_of(vals); } + struct CompilationUnit { + Source source; + ref env; + }; + + static map modules; + Value use(ref env, const Value& term) { if (!tail(term).is_list() || !head(tail(term)).is_symbol()) { err(tail(term).loc(), "Expected symbol in use expression, given '", @@ -968,21 +996,31 @@ namespace basil { string path = symbol_for(h.get_symbol()); path += ".bl"; - Source src((const char*)path.raw()); - if (!src.begin().peek()) { - err(h.loc(), "Could not load source file at path '", path, "'."); - return error(); + ref module; + auto it = modules.find(path); + if (it != modules.end()) { + module = it->second.env; } - ref module = load(src); - if (error_count()) return error(); - - for (const auto& p : *module) { - if (env->find(p.first)) { - err(term.loc(), "Module '", symbol_for(h.get_symbol()), - "' redefines '", p.first, "' in the current environment."); + else { + CompilationUnit unit { Source((const char*)path.raw()), {} }; + if (!unit.source.begin().peek()) { + err(h.loc(), "Could not load source file at path '", path, "'."); return error(); } + module = unit.env = load(unit.source); + if (error_count()) return error(); + + for (const auto& p : *module) { + if (env->find(p.first)) { + err(term.loc(), "Module '", symbol_for(h.get_symbol()), + "' redefines '", p.first, "' in the current environment."); + return error(); + } + } + + modules.put(path, unit); } + env->import(module); return Value(VOID); } @@ -1033,10 +1071,12 @@ namespace basil { u32 i = 0; while (v->is_list()) { if (i < first.get_function().arity() - && first.get_function().args()[i] & KEYWORD_ARG_BIT - && v->get_list().head().is_symbol()) + && first.get_function().args()[i] & KEYWORD_ARG_BIT + && v->get_list().head().is_symbol()) { args.push(v->get_list().head()); // leave keywords quoted + } else args.push(eval(env, v->get_list().head())); + v = &v->get_list().tail(); i ++; } @@ -1048,10 +1088,32 @@ namespace basil { } return call(env, first, Value(new ProductValue(args))); } + else if (first.is_runtime() && + first.get_runtime()->type()->kind() == KIND_FUNCTION) { + vector args; + Value args_term = tail(term); + const Value* v = &args_term; + u32 i = 0; + while (v->is_list()) { + args.push(eval(env, v->get_list().head())); + + v = &v->get_list().tail(); + i ++; + } + const FunctionType* fntype = (const FunctionType*)first.get_runtime()->type(); + const ProductType* args_type = (const ProductType*)fntype->arg(); + if (args.size() != args_type->count()) { + err(term.loc(), "Procedure expects ", + args_type->count(), " arguments, ", + args.size(), " provided."); + return error(); + } + return call(env, first, Value(new ProductValue(args))); + } if (tail(term).is_void()) return first; - err(term.loc(), "Could not evaluate list."); + err(term.loc(), "Could not evaluate list '", term, "'."); return error(); } diff --git a/example/factorial.bl b/example/factorial.bl new file mode 100644 index 0000000..cc739a7 --- /dev/null +++ b/example/factorial.bl @@ -0,0 +1,3 @@ +use std/math + +display 10 factorial \ No newline at end of file diff --git a/example/fibonacci.bl b/example/fibonacci.bl new file mode 100644 index 0000000..80a5008 --- /dev/null +++ b/example/fibonacci.bl @@ -0,0 +1,5 @@ +def (fib n) + if n < 2 n + :else (fib n - 1) + (fib n - 2) + +display (fib 30) \ No newline at end of file diff --git a/example/guessing.bl b/example/guessing.bl new file mode 100644 index 0000000..5c87e30 --- /dev/null +++ b/example/guessing.bl @@ -0,0 +1,17 @@ +display "Enter a seed." + +def (scramble x) + (x * 62192187 + 29681) % 1000 + +def secret (scramble (read-int)) +def guess 0 + +while guess != secret + display "Guess the number!" + guess = (read-int) + if guess == secret + display "You got it!" + :elif guess < secret + display "Too low!" + :else + display "Too high!" diff --git a/example/list-ops.bl b/example/list-ops.bl new file mode 100644 index 0000000..aba676d --- /dev/null +++ b/example/list-ops.bl @@ -0,0 +1,37 @@ +use std/list + +def nums 1 .. 20 + +def (even? x) x % 2 == 0 +def (odd? x) x % 2 == 1 + +def (add x y) x + y +def (multiply x y) x * y + +display "Even number squares from 1 to 20:" +display nums filter even? map (lambda (x) x * x) +display "" + +display "Sum of odd numbers from 1 to 20 that aren't divisible by 5:" +display nums filter odd? filter (lambda (x) x % 5 != 0) reduce add +display "" + +display "Middle 10 of numbers from 1 to 20:" +display nums take 15 drop 5 +display "" + +def (prime? x) + def factor 2 + def result x > 1 + while factor <= x / 2 + if x % factor == 0 + result = false + :else [] + factor = factor + 1 + result + +display nums filter prime? + +display "Product of prime numbers between 1 and 20 (2 * 3 * 5 * 7 * 11 * 13 * 17):" +display nums filter prime? reduce multiply +display "" \ No newline at end of file diff --git a/main.cpp b/main.cpp index bde804f..e1f1e47 100644 --- a/main.cpp +++ b/main.cpp @@ -220,6 +220,12 @@ Value intro_set_section(ref env, const Value& args) { args.get_product()[0]); return Value(ERROR); } + if (args.get_product()[0].get_int() < 1 + || args.get_product()[0].get_int() > 7) { + err(args.get_product()[0].loc(), "Invalid section number. Must be between 1 ", + "and 7."); + return Value(ERROR); + } intro_section = args.get_product()[0].get_int() - 1; intro_section_index = 0; return intro_start(env, args); @@ -293,7 +299,7 @@ R"(Lists can be useful for storing data, but they can also be used to represent code. Basil programs themselves are just lists of terms that the Basil compiler evaluates. To write a list we want to be evaluated, we enclose other -terms in parentheses. +terms in parentheses, and separate them with spaces. )", R"(Type '(+ 1 2)' into the prompt, then press Enter twice. )", Value(3)}); @@ -306,7 +312,8 @@ is 3. Writing all these parentheses can be kind of a pain! So Basil treats lines on the top level of the program as -lists, as if they were surrounded by parentheses. +lists, as if they were surrounded by parentheses. We still +need to separate every term with spaces, though. )", R"(Type '* 3 3' into the prompt, then press Enter twice. )", Value(9)}); @@ -764,8 +771,7 @@ mixfix, with any number of keywords. This applies to both R"(Enter: macro (print expr :if cond) if |cond| - display |expr| - :else [] + display |expr| print "hello" if (3 > 2) into the prompt. )", runtime(VOID)}); @@ -815,8 +821,8 @@ R"(Congratulations! You've completed the Basil interactive tour! If you'd like to continue to play around with the -language, try running './basil help' in command line -for a full list of Basil commands! +language, try quitting this tour and running './basil help' +in command line for a full list of Basil commands! If you really liked the project, consider: - starring it on GitHub! https://github.com/basilTeam/basil diff --git a/native.cpp b/native.cpp index 26df509..a039624 100644 --- a/native.cpp +++ b/native.cpp @@ -23,6 +23,15 @@ namespace basil { return result; } + i64 _listlen(void* list) { + u32 size = 0; + while (list) { + list = *((void**)list + 1); + size ++; + } + return size; + } + void _display_int(i64 value) { println(value); } @@ -91,6 +100,12 @@ namespace basil { return *(const unsigned char*)a - *(const unsigned char*)b; } + i64 _strlen(const char *s) { + const char *copy = s; + while (*s++); + return s - copy - 1; + } + const u8* _read_line() { string s; while (_stdin.peek() != '\n') { s += _stdin.read(); } @@ -100,11 +115,37 @@ namespace basil { return buf; } + i64 _read_int() { + i64 i; + read(_stdin, i); + return i; + } + + const u8* _read_word() { + string s; + read(_stdin, s); + u8* buf = new u8[s.size() + 1]; + for (u32 i = 0; i < s.size(); i ++) buf[i] = s[i]; + buf[s.size()] = '\0'; + return buf; + } + + u8 _char_at(const char *s, i64 idx) { + return s[idx]; + } + + // const u8* _substr(const char *s, i64 start, i64 end) + void add_native_functions(Object& object) { add_native_function(object, "_cons", (void*)_cons); add_native_function(object, "_strcmp", (void*)_strcmp); + add_native_function(object, "_strlen", (void*)_strlen); add_native_function(object, "_read_line", (void*)_read_line); + add_native_function(object, "_read_int", (void*)_read_int); + add_native_function(object, "_read_word", (void*)_read_word); + add_native_function(object, "_char_at", (void*)_char_at); + add_native_function(object, "_listlen", (void*)_listlen); add_native_function(object, "_display_int", (void*)_display_int); add_native_function(object, "_display_symbol", (void*)_display_symbol); diff --git a/ssa.cpp b/ssa.cpp index 589c153..c52ba36 100644 --- a/ssa.cpp +++ b/ssa.cpp @@ -602,20 +602,25 @@ namespace basil { write(io, "$", _index, " = ", _src); } - CallInsn::CallInsn(u32 label, const Type* ret): - _label(label), _ret(ret) {} + CallInsn::CallInsn(Location fn, const Type* ret): + _fn(fn), _ret(ret) {} Location CallInsn::lazy_loc() { return _func->create_local(_ret); } void CallInsn::emit() { - call(label64(symbol_for_label(_label, GLOBAL_SYMBOL))); + if (_fn.type == SSA_LABEL) + call(label64(symbol_for_label(_fn.label_index, GLOBAL_SYMBOL))); + else { + mov(r64(RAX), x64_arg(_fn)); + call(r64(RAX)); + } mov(x64_arg(_loc), r64(RAX)); } void CallInsn::format(stream& io) const { - write(io, _loc, " = ", all_labels[_label], "()"); + write(io, _loc, " = ", _fn, "()"); } Label::Label(u32 label): @@ -686,8 +691,10 @@ void write(stream& io, const basil::Location& loc) { return; case basil::SSA_LABEL: write(io, basil::all_labels[loc.label_index]); + return; case basil::SSA_CONSTANT: write(io, basil::all_constants[loc.constant_index].name); + return; default: return; } diff --git a/ssa.h b/ssa.h index 6ecfbe9..e73d0a6 100644 --- a/ssa.h +++ b/ssa.h @@ -336,12 +336,12 @@ namespace basil { }; class CallInsn : public Insn { - u32 _label; + Location _fn; const Type* _ret; protected: Location lazy_loc() override; public: - CallInsn(u32 label, const Type* ret); + CallInsn(Location fn, const Type* ret); void emit() override; void format(stream& io) const override; diff --git a/std/list.bl b/std/list.bl index 987efd8..d7c6059 100644 --- a/std/list.bl +++ b/std/list.bl @@ -1,5 +1,5 @@ -infix (list length) - if list empty? 0 else (list tail length) + 1 +# infix (list length) +# if list empty? 0 else (list tail length) + 1 def (append a b) if a empty? b else a.head :: (append a.tail b) @@ -20,6 +20,26 @@ def (reverse-internal list acc) if list empty? acc :else (reverse-internal list.tail list.head :: acc) +infix (xs map f) + if xs empty? [] else + (f xs head) :: (xs tail map f) + +infix (xs filter f) + if xs empty? [] else + if (f xs head) + xs head :: (xs tail filter f) + :else + xs tail filter f + +infix 10 (x0 .. xN) + if x0 == xN [xN] else x0 :: (x0 + 1 .. xN) + +infix (xs reduce f) + if xs tail empty? + xs head + :else + f xs head +(xs tail reduce f) # little bit of a hack to help type inference + def (merge a b) if a empty? b :elif b empty? a diff --git a/std/math.bl b/std/math.bl new file mode 100644 index 0000000..173b603 --- /dev/null +++ b/std/math.bl @@ -0,0 +1,10 @@ +infix 45 (x ^ y) + if y < 0 1 / (x ^ -y) + :elif y == 0 1 + :else x * x ^ (y - 1) + +infix 45 (x squared) x * x +infix 45 (x cubed) x * x * x + +infix (x factorial) + if x == 0 1 else x - 1 factorial * x \ No newline at end of file diff --git a/tests/example.bl b/tests/example.bl deleted file mode 100644 index 73c564d..0000000 --- a/tests/example.bl +++ /dev/null @@ -1,6 +0,0 @@ -( ) { } [ ] ; | # delimiters -1 24 0591 # integers -abc Abc123 ++ =!= # symbols -+12 -(1 + 2) # prefix operators -a . b ... # dot (first is dot token, second is symbol) -:quoted :: n : int # various colon usage diff --git a/type.cpp b/type.cpp index 2f09a3e..ecebe3d 100644 --- a/type.cpp +++ b/type.cpp @@ -12,6 +12,10 @@ namespace basil { return this != ANY; } + const Type* Type::concretify() const { + return this; + } + SingletonType::SingletonType(const string& repr) : Type(::hash(repr)), _repr(repr) {} TypeKind SingletonType::kind() const { @@ -33,6 +37,10 @@ namespace basil { return _element->concrete(); } + const Type* ListType::concretify() const { + return find(_element->concretify()); + } + const Type* ListType::element() const { return _element; } @@ -100,6 +108,17 @@ namespace basil { const Type* ProductType::member(u32 i) const { return _members[i]; } + + bool ProductType::concrete() const { + for (const Type* t : _members) if (!t->concrete()) return false; + return true; + } + + const Type* ProductType::concretify() const { + vector ts = _members; + for (const Type*& t : ts) t = t->concretify(); + return find(ts); + } TypeKind ProductType::kind() const { return KIND_PRODUCT; @@ -141,6 +160,14 @@ namespace basil { return _arg->kind() == KIND_PRODUCT ? ((const ProductType*)_arg)->count() : 1; } + + bool FunctionType::concrete() const { + return _arg->concrete() && _ret->concrete(); + } + + const Type* FunctionType::concretify() const { + return find(_arg->concretify(), _ret->concretify()); + } TypeKind FunctionType::kind() const { return KIND_FUNCTION; @@ -246,6 +273,12 @@ namespace basil { return type_variables[_id]->concrete(); } + const Type* TypeVariable::concretify() const { + const Type* t = actual(); + if (t == ANY) return this; + return t->concretify(); + } + TypeKind TypeVariable::kind() const { return KIND_TYPEVAR; } @@ -298,20 +331,55 @@ namespace basil { else if (b == ANY) return a; if (a->kind() == KIND_TYPEVAR) { - if (!((const TypeVariable*)a)->actual()->concrete()) - return ((const TypeVariable*)a)->bind(b), b; - a = ((const TypeVariable*)a)->actual(); + if (!((const TypeVariable*)a)->actual()->concrete()) { + if (b->concrete() || b->kind() != KIND_TYPEVAR) + return ((const TypeVariable*)a)->bind(b), b; + else if (b > a) + return ((const TypeVariable*)a)->bind(b), b; + else return a; + } + else a = ((const TypeVariable*)a)->actual(); } if (b->kind() == KIND_TYPEVAR) { - if (!((const TypeVariable*)b)->actual()->concrete()) - return ((const TypeVariable*)b)->bind(a), a; + if (!((const TypeVariable*)b)->actual()->concrete()) { + if (a->concrete() || a->kind() != KIND_TYPEVAR) + return ((const TypeVariable*)b)->bind(a), a; + else if (a > b) + return ((const TypeVariable*)b)->bind(a), a; + else return b; + } b = ((const TypeVariable*)b)->actual(); } - if (a->kind() == KIND_LIST && b->kind() == KIND_LIST) - return find(unify(((const ListType*)a)->element(), - ((const ListType*)b)->element())); + if (a->kind() == KIND_LIST && b->kind() == KIND_LIST) { + const Type* elt = unify(((const ListType*)a)->element(), + ((const ListType*)b)->element()); + if (!elt) return nullptr; + return find(elt); + } + + if (a->kind() == KIND_PRODUCT && b->kind() == KIND_PRODUCT) { + vector members; + if (((const ProductType*)a)->count() != ((const ProductType*)b)->count()) + return nullptr; + for (u32 i = 0; i < ((const ProductType*)a)->count(); i ++) { + const Type* member = unify(((const ProductType*)a)->member(i), + ((const ProductType*)b)->member(i)); + if (!member) return nullptr; + members.push(member); + } + return find(members); + } + + if (a->kind() == KIND_FUNCTION && b->kind() == KIND_FUNCTION) { + const Type* arg = unify(((const FunctionType*)a)->arg(), + ((const FunctionType*)b)->arg()); + const Type* ret = unify(((const FunctionType*)a)->ret(), + ((const FunctionType*)b)->ret()); + if (!arg || !ret) return nullptr; + return find(arg, ret); + } if (a == VOID && b->kind() == KIND_LIST) return b; if (b == VOID && a->kind() == KIND_LIST) return a; diff --git a/type.h b/type.h index 40a0e1a..8a7a5f9 100644 --- a/type.h +++ b/type.h @@ -31,6 +31,7 @@ namespace basil { virtual TypeKind kind() const = 0; u64 hash() const; virtual bool concrete() const; + virtual const Type* concretify() const; virtual bool operator==(const Type& other) const = 0; virtual void format(stream& io) const = 0; }; @@ -52,6 +53,7 @@ namespace basil { const Type* element() const; bool concrete() const override; + const Type* concretify() const override; TypeKind kind() const override; bool operator==(const Type& other) const override; void format(stream& io) const override; @@ -75,6 +77,8 @@ namespace basil { u32 count() const; const Type* member(u32 i) const; + bool concrete() const override; + const Type* concretify() const override; TypeKind kind() const override; bool operator==(const Type& other) const override; void format(stream& io) const override; @@ -88,6 +92,8 @@ namespace basil { const Type* arg() const; const Type* ret() const; u32 arity() const; + bool concrete() const override; + const Type* concretify() const override; TypeKind kind() const override; bool operator==(const Type& other) const override; void format(stream& io) const override; @@ -127,6 +133,7 @@ namespace basil { class TypeVariable : public Type { u32 _id; + friend void bind(const Type* a, const Type* b); protected: TypeVariable(u32 id); public: @@ -135,6 +142,7 @@ namespace basil { const Type* actual() const; void bind(const Type* concrete) const; bool concrete() const override; + const Type* concretify() const override; TypeKind kind() const override; bool operator==(const Type& other) const override; void format(stream& io) const override; @@ -154,7 +162,7 @@ namespace basil { extern const Type *INT, *SYMBOL, *VOID, *ERROR, *TYPE, *ALIAS, *BOOL, *ANY, *STRING; - + const Type* unify(const Type* a, const Type* b); } diff --git a/values.cpp b/values.cpp index c54f1fd..5fa88c8 100644 --- a/values.cpp +++ b/values.cpp @@ -541,6 +541,10 @@ namespace basil { for (auto& p : *_insts) p.second->dec(); delete _insts; } + if (_calls) { + for (auto& p : *_calls) ((FunctionValue*)p)->dec(); + delete _calls; + } } FunctionValue::FunctionValue(const FunctionValue& other): @@ -552,6 +556,7 @@ namespace basil { _calls(other._calls ? new set(*other._calls) : nullptr) { if (_insts) for (auto& p : *_insts) p.second->inc(); + if (_calls) for (auto p : *_calls) ((FunctionValue*)p)->inc(); } FunctionValue& FunctionValue::operator=(const FunctionValue& other) { @@ -560,6 +565,10 @@ namespace basil { for (auto& p : *_insts) p.second->dec(); delete _insts; } + if (_calls) { + for (auto& p : *_calls) ((FunctionValue*)p)->dec(); + delete _calls; + } _name = other._name; _code = other._code.clone(); _builtin = other._builtin; @@ -568,7 +577,10 @@ namespace basil { _insts = other._insts ? new map(*other._insts) : nullptr; + _calls = other._calls ? + new set(*other._calls) : nullptr; if (_insts) for (auto& p : *_insts) p.second->inc(); + if (_calls) for (auto& p : *_calls) ((FunctionValue*)p)->inc(); } return *this; } @@ -607,7 +619,8 @@ namespace basil { void FunctionValue::add_call(const FunctionValue* other) { if (!_calls) _calls = new set(); - if (other->_calls) for (const FunctionValue* f : *other->_calls) { + if (other != this && other->_calls) + for (const FunctionValue* f : *other->_calls) { _calls->insert(f); ((FunctionValue*)f)->inc(); // evil } @@ -963,8 +976,41 @@ namespace basil { return Value(ERROR); } - Value read_line() { - return new ASTReadLine(NO_LOCATION); + Value length(const Value& val) { + if (val.is_error()) return error(); + + if (val.is_runtime()) + return new ASTLength(val.loc(), lower(val).get_runtime()); + + if (!val.is_string() && !val.is_list()) { + err(val.loc(), "Expected string or list, given '", val.type(), "'."); + return error(); + } + + if (val.is_string()) return Value(i64(val.get_string().size())); + else return Value(i64(to_vector(val).size())); + } + + Value char_at(const Value& str, const Value& idx) { + if (str.is_runtime() || idx.is_runtime()) { + vector args; + Value s = lower(str), i = lower(idx); + args.push(s.get_runtime()); + args.push(i.get_runtime()); + vector arg_types; + arg_types.push(STRING); + arg_types.push(INT); + return new ASTNativeCall(str.loc(), "_char_at", INT, args, arg_types); + } + if (!str.is_string()) { + err(str.loc(), "Expected string, given '", str.type(), "'."); + return error(); + } + if (!idx.is_int()) { + err(idx.loc(), "Expected integer to index string, given '", str.type(), "'."); + return error(); + } + return Value(i64(str.get_string()[idx.get_int()])); } Value type_of(const Value& v) { @@ -986,7 +1032,8 @@ namespace basil { new_args.push(fn.args()[i]); } } - Value v = eval(new_env, fn.body().clone()); + Value cloned = fn.body().clone(); + Value v = eval(new_env, cloned); if (v.is_error()) return nullptr; if (!v.is_runtime()) v = lower(v); ASTNode* result = new ASTFunction(loc, new_env, args_type, @@ -1020,6 +1067,51 @@ namespace basil { } Value call(ref env, Value& function, const Value& arg) { + if (function.is_runtime()) { + u32 argc = arg.get_product().size(); + vector argts; + vector lowered_args; + for (u32 i = 0; i < argc; i ++) { + if (arg.get_product()[i].is_function()) { + vector inner_argts; + for (u32 j = 0; j < arg.get_product()[i].get_function().arity(); j ++) + inner_argts.push(find()); + argts.push(find( + find(inner_argts), find())); + lowered_args.push(arg.get_product()[i]); // we'll lower this later + } + else { + Value lowered = lower(arg.get_product()[i]); + argts.push(((const RuntimeType*)lowered.type())->base()); + lowered_args.push(lowered); + } + } + const Type* argt = find(argts); + vector arg_nodes; + for (u32 i = 0; i < lowered_args.size(); i ++) { + if (lowered_args[i].is_function()) { + const Type* t = ((const ProductType*)argt)->member(i); + if (!t->concrete() || t->kind() != KIND_FUNCTION) { + err(lowered_args[i].loc(), "Could not deduce type for function ", + "parameter, resolved to '", t, "'."); + return error(); + } + const Type* fnarg = ((const FunctionType*)t)->arg(); + FunctionValue& fn = lowered_args[i].get_function(); + ASTNode* argbody = fn.instantiation(fnarg); + if (!argbody) { + fn.instantiate(fnarg, new ASTIncompleteFn(lowered_args[i].loc(), + fnarg, fn.name())); + argbody = instantiate(lowered_args[i].loc(), fn, fnarg); + } + if (!argbody) return error(); + arg_nodes.push(argbody); + } + else arg_nodes.push(lowered_args[i].get_runtime()); + } + return new ASTCall(function.loc(), function.get_runtime(), arg_nodes); + } + if (!function.is_function() && !function.is_error()) { err(function.loc(), "Called value is not a procedure."); return error(); @@ -1068,9 +1160,19 @@ namespace basil { } } else { - Value lowered = lower(arg.get_product()[i]); - argts.push(((const RuntimeType*)lowered.type())->base()); - lowered_args.push(lowered); + if (arg.get_product()[i].is_function()) { + vector inner_argts; + for (u32 j = 0; j < arg.get_product()[i].get_function().arity(); j ++) + inner_argts.push(find()); + argts.push(find( + find(inner_argts), find())); + lowered_args.push(arg.get_product()[i]); // we'll lower this later + } + else { + Value lowered = lower(arg.get_product()[i]); + argts.push(((const RuntimeType*)lowered.type())->base()); + lowered_args.push(lowered); + } } } const Type* argt = find(argts); @@ -1081,7 +1183,30 @@ namespace basil { } if (!body) return error(); vector arg_nodes; - for (Value& v : lowered_args) arg_nodes.push(v.get_runtime()); + for (u32 i = 0; i < lowered_args.size(); i ++) { + if (lowered_args[i].is_function()) { + const Type* t = ((const ProductType*)argt)->member(i); + if (t->kind() != KIND_FUNCTION || + !((const FunctionType*)t)->arg()->concrete()) { + err(lowered_args[i].loc(), "Could not deduce type for function ", + "parameter, resolved to '", t, "'."); + return error(); + } + const Type* fnarg = ((const FunctionType*)t)->arg(); + while (fnarg->kind() == KIND_TYPEVAR) + fnarg = ((const TypeVariable*)fnarg)->actual(); + FunctionValue& fn = lowered_args[i].get_function(); + ASTNode* argbody = fn.instantiation(fnarg); + if (!argbody) { + fn.instantiate(fnarg, new ASTIncompleteFn(lowered_args[i].loc(), + fnarg, fn.name())); + argbody = instantiate(lowered_args[i].loc(), fn, fnarg); + } + if (!argbody) return error(); + arg_nodes.push(argbody); + } + else arg_nodes.push(lowered_args[i].get_runtime()); + } return new ASTCall(function.loc(), body, arg_nodes); } diff --git a/values.h b/values.h index 7486bc1..c22f1c6 100644 --- a/values.h +++ b/values.h @@ -263,7 +263,10 @@ namespace basil { Value error(); + Value length(const Value& str); + Value read_line(); + Value char_at(const Value& str, const Value& idx); Value type_of(const Value& v); From 648530ef32feb899b7c63ff2b2999aa9e0a06ac9 Mon Sep 17 00:00:00 2001 From: Alec Minchington Date: Wed, 14 Oct 2020 20:16:27 -0400 Subject: [PATCH 05/17] Add .clang-format file --- .clang-format | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..2dfa0b9 --- /dev/null +++ b/.clang-format @@ -0,0 +1,17 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +ColumnLimit: 120 +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: Always +AllowShortLoopsOnASingleLine: true +AlwaysBreakTemplateDeclarations: Yes +FixNamespaceComments: true +IndentCaseLabels: true +IndentPPDirectives: BeforeHash +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +PointerAlignment: Left +UseTab: Never From 2ade60dae8fdbb79a7179e6c0c7126e4e1081490 Mon Sep 17 00:00:00 2001 From: elucent <9532786+elucent@users.noreply.github.com> Date: Fri, 8 Jan 2021 23:45:03 -0500 Subject: [PATCH 06/17] Pushing lots of uncommitted changes. Still very WIP. --- .gitignore | 5 +- Makefile | 11 +- README.md | 3 +- ast.cpp => compiler/ast.cpp | 409 ++++++++++----- ast.h => compiler/ast.h | 82 ++- driver.cpp => compiler/driver.cpp | 90 +++- driver.h => compiler/driver.h | 7 +- env.cpp => compiler/env.cpp | 3 +- env.h => compiler/env.h | 4 +- errors.cpp => compiler/errors.cpp | 0 errors.h => compiler/errors.h | 0 eval.cpp => compiler/eval.cpp | 254 +++++++--- eval.h => compiler/eval.h | 0 compiler/ir.cpp | 802 ++++++++++++++++++++++++++++++ ssa.h => compiler/ir.h | 152 ++++-- lex.cpp => compiler/lex.cpp | 8 +- lex.h => compiler/lex.h | 2 +- main.cpp => compiler/main.cpp | 33 +- native.cpp => compiler/native.cpp | 138 +++-- native.h => compiler/native.h | 2 +- compiler/ops.cpp | 545 ++++++++++++++++++++ compiler/ops.h | 50 ++ parse.cpp => compiler/parse.cpp | 23 + parse.h => compiler/parse.h | 0 source.cpp => compiler/source.cpp | 0 source.h => compiler/source.h | 0 compiler/ssa.cpp | 507 +++++++++++++++++++ compiler/ssa.h | 284 +++++++++++ type.cpp => compiler/type.cpp | 119 ++++- type.h => compiler/type.h | 34 +- values.cpp => compiler/values.cpp | 256 +++++++++- values.h => compiler/values.h | 43 +- core.cpp | 139 ++++++ example/fibonacci | Bin 0 -> 4920 bytes example/fibonacci.bl | 2 +- example/fibonacci.c | 8 + example/fibonaccic | Bin 0 -> 9248 bytes example/list-ops.bl | 4 +- jasmine/obj.cpp | 193 ++++++- jasmine/obj.h | 1 + jasmine/sym.cpp | 2 +- jasmine/sym.h | 1 - jasmine/utils.cpp | 2 +- jasmine/utils.h | 2 +- jasmine/x64.cpp | 258 +++++----- jasmine/x64.h | 6 +- runtime.cpp | 0 ssa.cpp | 713 -------------------------- std/math.bl | 8 +- util/defs.h | 3 +- util/hash.h | 1 - util/io.cpp | 1 - util/io.h | 9 + util/rc.h | 67 ++- util/slice.h | 2 + util/str.cpp | 1 - util/utils.cpp | 14 + util/utils.h | 10 + util/vec.h | 19 +- 59 files changed, 4064 insertions(+), 1268 deletions(-) rename ast.cpp => compiler/ast.cpp (66%) rename ast.h => compiler/ast.h (74%) rename driver.cpp => compiler/driver.cpp (71%) rename driver.h => compiler/driver.h (71%) rename env.cpp => compiler/env.cpp (98%) rename env.h => compiler/env.h (96%) rename errors.cpp => compiler/errors.cpp (100%) rename errors.h => compiler/errors.h (100%) rename eval.cpp => compiler/eval.cpp (82%) rename eval.h => compiler/eval.h (100%) create mode 100644 compiler/ir.cpp rename ssa.h => compiler/ir.h (71%) rename lex.cpp => compiler/lex.cpp (96%) rename lex.h => compiler/lex.h (95%) rename main.cpp => compiler/main.cpp (97%) rename native.cpp => compiler/native.cpp (52%) rename native.h => compiler/native.h (93%) create mode 100644 compiler/ops.cpp create mode 100644 compiler/ops.h rename parse.cpp => compiler/parse.cpp (89%) rename parse.h => compiler/parse.h (100%) rename source.cpp => compiler/source.cpp (100%) rename source.h => compiler/source.h (100%) create mode 100644 compiler/ssa.cpp create mode 100644 compiler/ssa.h rename type.cpp => compiler/type.cpp (74%) rename type.h => compiler/type.h (80%) rename values.cpp => compiler/values.cpp (81%) rename values.h => compiler/values.h (86%) create mode 100644 core.cpp create mode 100644 example/fibonacci create mode 100644 example/fibonacci.c create mode 100644 example/fibonaccic create mode 100644 runtime.cpp delete mode 100644 ssa.cpp create mode 100644 util/utils.cpp create mode 100644 util/utils.h diff --git a/.gitignore b/.gitignore index bd48028..dcb3500 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ -*.o -basil .vscode/ +TODO +*.o +basil \ No newline at end of file diff --git a/Makefile b/Makefile index 8e7a87b..fda33ee 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,12 @@ -SRCS := $(wildcard *.cpp) $(wildcard util/*.cpp) $(wildcard jasmine/*.cpp) +SRCS := $(wildcard *.cpp) \ + $(wildcard compiler/*.cpp) \ + $(wildcard util/*.cpp) \ + $(wildcard jasmine/*.cpp) OBJS := $(patsubst %.cpp,%.o,$(SRCS)) CXX := clang++ -CXXHEADERS := -I. -Iutil -Ijasmine -CXXFLAGS := $(CXXHEADERS) -std=c++17 -ffast-math -fno-rtti -fno-exceptions -Wno-null-dereference +CXXHEADERS := -I. -Iutil -Ijasmine -Icompiler +CXXFLAGS := $(CXXHEADERS) -std=c++17 -ffast-math -fno-rtti -fno-exceptions -Wno-null-dereference -Wl,--unresolved-symbols=ignore-in-object-files clean: rm -f $(OBJS) *.o.tmp basil @@ -19,4 +22,4 @@ release: $(OBJS) $(CXX) $(CXXFLAGS) $^ -o basil %.o: %.cpp %.h - $(CXX) $(CXXFLAGS) -c $< -o $@ \ No newline at end of file + $(CXX) $(CXXFLAGS) -c $< -o $@ diff --git a/README.md b/README.md index b31a5b5..eab0944 100644 --- a/README.md +++ b/README.md @@ -494,7 +494,8 @@ specified in terms of the types they accept and produce. | `read-word` | `() -> String` | Reads a space-delimited string from standard input. | | `read-int` | `() -> Int` | Reads an integer from standard input. | | `length` | `String | 'T0 List -> Int` | Returns the length of a string or list. | -| +| `at` | `String * Int -> Int` | Returns the character code at an index in a string. | +| `^` | `String * String -> String` | Concatenates two strings. | 1. Value equality for integers, bools, strings, and symbols; reference equality for lists. diff --git a/ast.cpp b/compiler/ast.cpp similarity index 66% rename from ast.cpp rename to compiler/ast.cpp index 777fd7d..3ed008d 100644 --- a/ast.cpp +++ b/compiler/ast.cpp @@ -25,8 +25,12 @@ namespace basil { return _type; } + ref ASTSingleton::emit(ref& parent) { + return nullptr; + } + Location ASTSingleton::emit(Function& func) { - return ssa_none(); + return loc_none(); } void ASTSingleton::format(stream& io) const { @@ -40,8 +44,12 @@ namespace basil { return VOID; } + ref ASTVoid::emit(ref& parent) { + return ref(parent); + } + Location ASTVoid::emit(Function& function) { - return ssa_immediate(0); + return loc_immediate(0); } void ASTVoid::format(stream& io) const { @@ -55,8 +63,12 @@ namespace basil { return INT; } + ref ASTInt::emit(ref& parent) { + return newref(parent, _value); + } + Location ASTInt::emit(Function& func) { - return ssa_immediate(_value); + return loc_immediate(_value); } void ASTInt::format(stream& io) const { @@ -70,8 +82,12 @@ namespace basil { return SYMBOL; } + ref ASTSymbol::emit(ref& parent) { + return newref(parent, _value); + } + Location ASTSymbol::emit(Function& func) { - return ssa_immediate(_value); + return loc_immediate(_value); } void ASTSymbol::format(stream& io) const { @@ -85,8 +101,12 @@ namespace basil { return STRING; } + ref ASTString::emit(ref& parent) { + return newref(parent, _value); + } + Location ASTString::emit(Function& func) { - return func.add(new AddressInsn(ssa_const(ssa_next_label(), _value), type())); + return func.add(new AddressInsn(const_loc(next_label(), _value), type())); } void ASTString::format(stream& io) const { @@ -100,8 +120,12 @@ namespace basil { return BOOL; } + ref ASTBool::emit(ref& parent) { + return newref(parent, _value); + } + Location ASTBool::emit(Function& func) { - return ssa_immediate(_value ? 1 : 0); + return loc_immediate(_value ? 1 : 0); } void ASTBool::format(stream& io) const { @@ -120,16 +144,40 @@ namespace basil { return ERROR; } + ref ASTVar::emit(ref& parent) { + return nullptr; // todo + } + Location ASTVar::emit(Function& func) { const Def* def = _env->find(symbol_for(_name)); - if (!def) return ssa_none(); - return func.add(new LoadInsn(def->location)); + if (!def) return loc_none(); + return def->location; + // return func.add(new LoadInsn(def->location)); <-- why? } void ASTVar::format(stream& io) const { write(io, symbol_for(_name)); } + ASTExtern::ASTExtern(SourceLocation loc): + ASTNode(loc) {} + + const Type* ASTExtern::lazy_type() { + return find(); + } + + ref ASTExtern::emit(ref& parent) { + return nullptr; // todo + } + + Location ASTExtern::emit(Function& function) { + return loc_none(); + } + + void ASTExtern::format(stream& io) const { + write(io, "extern"); + } + ASTUnary::ASTUnary(SourceLocation loc, ASTNode* child): ASTNode(loc), _child(child) { _child->inc(); @@ -165,6 +213,23 @@ namespace basil { return result; } + ref ASTBinaryMath::emit(ref& parent) { + switch (_op) { + case AST_ADD: + return newref(parent, type(), SSA_ADD, _left->emit(parent), _right->emit(parent)); + case AST_SUB: + return newref(parent, type(), SSA_SUB, _left->emit(parent), _right->emit(parent)); + case AST_MUL: + return newref(parent, type(), SSA_MUL, _left->emit(parent), _right->emit(parent)); + case AST_DIV: + return newref(parent, type(), SSA_DIV, _left->emit(parent), _right->emit(parent)); + case AST_REM: + return newref(parent, type(), SSA_REM, _left->emit(parent), _right->emit(parent)); + default: + return nullptr; + } + } + Location ASTBinaryMath::emit(Function& func) { switch (_op) { case AST_ADD: @@ -178,7 +243,7 @@ namespace basil { case AST_REM: return func.add(new RemInsn(_left->emit(func), _right->emit(func))); default: - return ssa_none(); + return loc_none(); } } @@ -208,6 +273,19 @@ namespace basil { return BOOL; } + ref ASTBinaryLogic::emit(ref& parent) { + switch (_op) { + case AST_AND: + return newref(parent, type(), SSA_AND, _left->emit(parent), _right->emit(parent)); + case AST_OR: + return newref(parent, type(), SSA_OR, _left->emit(parent), _right->emit(parent)); + case AST_XOR: + return newref(parent, type(), SSA_XOR, _left->emit(parent), _right->emit(parent)); + default: + return nullptr; + } + } + Location ASTBinaryLogic::emit(Function& func) { switch (_op) { case AST_AND: @@ -217,7 +295,7 @@ namespace basil { case AST_XOR: return func.add(new XorInsn(_left->emit(func), _right->emit(func))); default: - return ssa_none(); + return loc_none(); } } @@ -242,6 +320,10 @@ namespace basil { return BOOL; } + ref ASTNot::emit(ref& parent) { + return newref(parent, type(), SSA_NOT, _child->emit(parent)); + } + Location ASTNot::emit(Function& func) { return func.add(new NotInsn(_child->emit(func))); } @@ -259,16 +341,28 @@ namespace basil { return BOOL; } + ref ASTBinaryEqual::emit(ref& parent) { + switch (_op) { + case AST_EQUAL: + return newref(parent, type(), SSA_EQ, _left->emit(parent), _right->emit(parent)); + case AST_INEQUAL: + return newref(parent, type(), SSA_NOT_EQ, _left->emit(parent), _right->emit(parent)); + default: + return nullptr; + } + } + Location ASTBinaryEqual::emit(Function& func) { - if (_left->type() == STRING || _right->type() == STRING) { - func.add(new StoreArgumentInsn(_left->emit(func), 0, _left->type())); - func.add(new StoreArgumentInsn(_right->emit(func), 1, _right->type())); + if (_left->type() == STRING || _right->type() == STRING) { + vector args; + args.push(_left->emit(func)); + args.push(_right->emit(func)); Location label; - label.type = SSA_LABEL; - label.label_index = ssa_find_label("_strcmp"); - Location result = func.add(new CallInsn(label, INT)); - return func.add(new EqualInsn(result, ssa_immediate(0))); - } + label.type = LOC_LABEL; + label.label_index = find_label("_strcmp"); + Location result = func.add(new CallInsn(label, args, INT)); + return func.add(new EqualInsn(result, loc_immediate(0))); + } switch (_op) { case AST_EQUAL: @@ -276,7 +370,7 @@ namespace basil { case AST_INEQUAL: return func.add(new InequalInsn(_left->emit(func), _right->emit(func))); default: - return ssa_none(); + return loc_none(); } } @@ -299,40 +393,56 @@ namespace basil { const Type* result = unify(lt, rt); if (result != INT) { - lt = unify(_left->type(), STRING); - rt = unify(_right->type(), STRING); - result = unify(lt, rt); - if (result != STRING) { - err(loc(), "Invalid parameters to relational expression: '", - _left->type(), "' and '", _right->type(), "'."); - return ERROR; - } + lt = unify(_left->type(), STRING); + rt = unify(_right->type(), STRING); + result = unify(lt, rt); + if (result != STRING) { + err(loc(), "Invalid parameters to relational expression: '", + _left->type(), "' and '", _right->type(), "'."); + return ERROR; + } } return BOOL; } + ref ASTBinaryRel::emit(ref& parent) { + switch (_op) { + case AST_LESS: + return newref(parent, type(), SSA_LESS, _left->emit(parent), _right->emit(parent)); + case AST_LESS_EQUAL: + return newref(parent, type(), SSA_LESS_EQ, _left->emit(parent), _right->emit(parent)); + case AST_GREATER: + return newref(parent, type(), SSA_GREATER, _left->emit(parent), _right->emit(parent)); + case AST_GREATER_EQUAL: + return newref(parent, type(), SSA_GREATER_EQ, _left->emit(parent), _right->emit(parent)); + default: + return nullptr; + } + } + Location ASTBinaryRel::emit(Function& func) { - if (_left->type() == STRING || _right->type() == STRING) { - func.add(new StoreArgumentInsn(_left->emit(func), 0, _left->type())); - func.add(new StoreArgumentInsn(_right->emit(func), 1, _right->type())); + if (_left->type() == STRING || _right->type() == STRING) { + vector args; + args.push(_left->emit(func)); + args.push(_right->emit(func)); Location label; - label.type = SSA_LABEL; - label.label_index = ssa_find_label("_strcmp"); - Location result = func.add(new CallInsn(label, INT)); - - switch(_op) { - case AST_LESS: - return func.add(new LessInsn(result, ssa_immediate(0))); - case AST_LESS_EQUAL: - return func.add(new LessEqualInsn(result, ssa_immediate(0))); - case AST_GREATER: - return func.add(new GreaterInsn(result, ssa_immediate(0))); - case AST_GREATER_EQUAL: - return func.add(new GreaterEqualInsn(result, ssa_immediate(0))); - default: - return ssa_none(); - } - } + label.type = LOC_LABEL; + label.label_index = find_label("_strcmp"); + Location result = func.add(new CallInsn(label, args, INT)); + + switch(_op) { + case AST_LESS: + return func.add(new LessInsn(result, loc_immediate(0))); + case AST_LESS_EQUAL: + return func.add(new LessEqualInsn(result, loc_immediate(0))); + case AST_GREATER: + return func.add(new GreaterInsn(result, loc_immediate(0))); + case AST_GREATER_EQUAL: + return func.add(new GreaterEqualInsn(result, loc_immediate(0))); + default: + return loc_none(); + } + } switch (_op) { case AST_LESS: @@ -344,7 +454,7 @@ namespace basil { case AST_GREATER_EQUAL: return func.add(new GreaterEqualInsn(_left->emit(func), _right->emit(func))); default: - return ssa_none(); + return loc_none(); } } @@ -363,11 +473,15 @@ namespace basil { return VOID; } + ref ASTDefine::emit(ref& parent) { + return newref(parent, _env, _name, _child->emit(parent)); + } + Location ASTDefine::emit(Function& func) { Location loc = func.create_local(symbol_for(_name), type()); _env->find(symbol_for(_name))->location = loc; - func.add(new StoreInsn(loc, _child->emit(func), true)); - return ssa_none(); + func.add(new StoreInsn(loc, _child->emit(func))); + return loc_none(); } void ASTDefine::format(stream& io) const { @@ -403,6 +517,12 @@ namespace basil { return ((const FunctionType*)fntype)->ret(); } + ref ASTCall::emit(ref& parent) { + vector> args; + for (ASTNode* n : _args) args.push(n->emit(parent)); + return newref(parent, type(), _func->emit(parent), args); + } + Location ASTCall::emit(Function& func) { Location fn = _func->emit(func); vector arglocs; @@ -411,14 +531,12 @@ namespace basil { arglocs.push(_args[i]->emit(func)); } for (u32 i = 0; i < _args.size(); i ++) { - if (arglocs[i].type == SSA_LABEL) { + if (arglocs[i].type == LOC_LABEL) { arglocs[i] = func.add(new AddressInsn(arglocs[i], ((const ProductType*)argt)->member(i))); } - func.add(new StoreArgumentInsn(arglocs[i], i, - ((const ProductType*)argt)->member(i))); } - return func.add(new CallInsn(fn, type())); + return func.add(new CallInsn(fn, arglocs, type())); } void ASTCall::format(stream& io) const { @@ -435,10 +553,7 @@ namespace basil { ASTNode(loc), _args(args), _name(name) {} Location ASTIncompleteFn::emit(Function& func) { - Location loc; - loc.type = SSA_LABEL; - loc.label_index = ssa_find_label(symbol_for(_name)); - return loc; + return loc_label(symbol_for(_name)); } void ASTIncompleteFn::format(stream& io) const { @@ -462,6 +577,10 @@ namespace basil { return find(_args_type, _body->type()); } + ref ASTFunction::emit(ref& parent) { + + } + Location ASTFunction::emit(Function& func) { if (!_emitted) { Function& fn = _name == -1 ? func.create_function() @@ -473,12 +592,13 @@ namespace basil { ((ProductType*)_args_type)->member(i))); } fn.add(new RetInsn(_body->emit(fn))); + fn.last()->succ().clear(); _emitted = true; } Location loc; - loc.type = SSA_LABEL; + loc.type = LOC_LABEL; loc.label_index = _label; return loc; } @@ -547,16 +667,22 @@ namespace basil { } Location ASTIf::emit(Function& func) { - u32 _else = ssa_next_label(), _end = ssa_next_label(); + u32 _else = next_label(), _end = next_label(); Location result = func.create_local(type()); func.add(new IfZeroInsn(_else, _cond->emit(func))); + Insn* ifz = func.last(); Location true_result = _if_true->emit(func); - func.add(new StoreInsn(result, true_result, true)); + func.add(new StoreInsn(result, true_result)); func.add(new GotoInsn(_end)); + Insn* skip = func.last(); func.add(new Label(_else)); + Insn* elselbl = func.last(); Location false_result = _if_false->emit(func); - func.add(new StoreInsn(result, false_result, true)); + func.add(new StoreInsn(result, false_result)); func.add(new Label(_end)); + Insn* end = func.last(); + ifz->succ().push(elselbl); // ifz -> else + skip->succ()[0] = end; // goto -> end return result; } @@ -587,13 +713,19 @@ namespace basil { } Location ASTWhile::emit(Function& func) { - u32 _start = ssa_next_label(), _end = ssa_next_label(); + u32 _start = next_label(), _end = next_label(); Location result = func.create_local(type()); func.add(new Label(_start)); + Insn* start = func.last(); func.add(new IfZeroInsn(_end, _cond->emit(func))); + Insn* ifz = func.last(); _body->emit(func); func.add(new GotoInsn(_start)); + Insn* loop = func.last(); func.add(new Label(_end)); + Insn* end = func.last(); + ifz->succ().push(end); + loop->succ()[0] = start; return result; } @@ -618,7 +750,7 @@ namespace basil { } Location ASTIsEmpty::emit(Function& func) { - return func.add(new EqualInsn(_child->emit(func), ssa_immediate(0))); + return func.add(new EqualInsn(_child->emit(func), loc_immediate(0))); } void ASTIsEmpty::format(stream& io) const { @@ -698,13 +830,13 @@ namespace basil { } Location ASTCons::emit(Function& func) { - Location l = _left->emit(func), r = _right->emit(func); - func.add(new StoreArgumentInsn(l, 0, _left->type())); - func.add(new StoreArgumentInsn(r, 1, _right->type())); + vector args; + args.push(_left->emit(func)); + args.push(_right->emit(func)); Location label; - label.type = SSA_LABEL; - label.label_index = ssa_find_label("_cons"); - return func.add(new CallInsn(label, type())); + label.type = LOC_LABEL; + label.label_index = find_label("_cons"); + return func.add(new CallInsn(label, args, type())); } void ASTCons::format(stream& io) const { @@ -724,24 +856,24 @@ namespace basil { return INT; } - ASTLength::ASTLength(SourceLocation loc, ASTNode* child) - : basil::ASTUnary(loc, child) {} + ASTLength::ASTLength(SourceLocation loc, ASTNode* child): + basil::ASTUnary(loc, child) {} - Location ASTLength::emit(Function& func) { - func.add(new StoreArgumentInsn(_child->emit(func), 0, _child->type())); + Location ASTLength::emit(Function& func) { + vector args; + args.push(_child->emit(func)); - Location label; - label.type = SSA_LABEL; - if (_child->type() == STRING) label.label_index = ssa_find_label("_strlen"); - else label.label_index = ssa_find_label("_listlen"); - - return func.add(new CallInsn(label, INT)); - } - - void ASTLength::format(stream& io) const { - write(io, "(length ", _child, ")"); - } + Location label; + label.type = LOC_LABEL; + if (_child->type() == STRING) label.label_index = find_label("_strlen"); + else label.label_index = find_label("_listlen"); + + return func.add(new CallInsn(label, args, INT)); + } + void ASTLength::format(stream& io) const { + write(io, "(length ", _child, ")"); + } const Type* ASTDisplay::lazy_type() { return VOID; @@ -761,22 +893,23 @@ namespace basil { else if (_child->type() == find(BOOL)) name = "_display_bool_list"; else if (_child->type() == find(STRING)) name = "_display_string_list"; else if (_child->type() == VOID) name = "_display_int_list"; - func.add(new StoreArgumentInsn(_child->emit(func), 0, _child->type())); + vector args; + args.push(_child->emit(func)); Location label; - label.type = SSA_LABEL; - label.label_index = ssa_find_label(name); - return func.add(new CallInsn(label, type())); + label.type = LOC_LABEL; + label.label_index = find_label(name); + return func.add(new CallInsn(label, args, type())); } void ASTDisplay::format(stream& io) const { write(io, "(display ", _child, ")"); } - ASTNativeCall::ASTNativeCall(SourceLocation loc, const string &func_name, const Type* ret) - : basil::ASTNode(loc), _func_name(func_name), _ret(ret) {} + ASTNativeCall::ASTNativeCall(SourceLocation loc, const string &func_name, const Type* ret): + basil::ASTNode(loc), _func_name(func_name), _ret(ret) {} - ASTNativeCall::ASTNativeCall(SourceLocation loc, const string &func_name, const Type *ret, const vector& args, const vector& arg_types) - : ASTNode(loc), _func_name(func_name), _ret(ret), _args(args), _arg_types(arg_types) { + ASTNativeCall::ASTNativeCall(SourceLocation loc, const string &func_name, const Type *ret, const vector& args, const vector& arg_types): + ASTNode(loc), _func_name(func_name), _ret(ret), _args(args), _arg_types(arg_types) { for (ASTNode* node : _args) node->inc(); } @@ -784,23 +917,24 @@ namespace basil { for (ASTNode* node : _args) node->dec(); } - const Type* ASTNativeCall::lazy_type() { - for (int i = 0; i < _args.size(); i ++) { - if (!unify(_args[i]->type(), _arg_types[i])) { - err(_args[i]->loc(), "Expected '", _arg_types[i], "', given '", _args[i]->type(), "'."); - } - } - return _ret; - } + const Type* ASTNativeCall::lazy_type() { + for (int i = 0; i < _args.size(); i ++) { + if (!unify(_args[i]->type(), _arg_types[i])) { + err(_args[i]->loc(), "Expected '", _arg_types[i], "', given '", _args[i]->type(), "'."); + } + } + return _ret; + } - Location ASTNativeCall::emit(Function& func) { - for (int i = 0; i < _args.size(); i ++) - func.add(new StoreArgumentInsn(_args[i]->emit(func), i, _args[i]->type())); + Location ASTNativeCall::emit(Function& func) { + vector args; + for (int i = 0; i < _args.size(); i ++) + args.push(_args[i]->emit(func)); Location label; - label.type = SSA_LABEL; - label.label_index = ssa_find_label(_func_name); - return func.add(new CallInsn(label, _ret)); - } + label.type = LOC_LABEL; + label.label_index = find_label(_func_name); + return func.add(new CallInsn(label, args, _ret)); + } void ASTNativeCall::format(stream& io) const { write(io, "(", _func_name); @@ -811,27 +945,58 @@ namespace basil { ASTAssign::ASTAssign(SourceLocation loc, const ref env, u64 dest, ASTNode* src): ASTUnary(loc, src), _env(env), _dest(dest) {} - const Type* ASTAssign::lazy_type() { - const Type* src_type = _child->type(); - const Def* def = _env->find(symbol_for(_dest)); - const Type* dest_type = ((const RuntimeType*)def->value.type())->base(); - if (src_type == ERROR || dest_type == ERROR) - return ERROR; - if (!unify(src_type, dest_type)) { - err(loc(), "Invalid arguments to assignment '", src_type, "' and '", dest_type, "'."); - return ERROR; - } - return VOID; + const Type* ASTAssign::lazy_type() { + const Type* src_type = _child->type(); + const Def* def = _env->find(symbol_for(_dest)); + const Type* dest_type = ((const RuntimeType*)def->value.type())->base(); + if (src_type == ERROR || dest_type == ERROR) + return ERROR; + if (!unify(src_type, dest_type)) { + err(loc(), "Invalid arguments to assignment '", src_type, "' and '", dest_type, "'."); + return ERROR; + } + return VOID; } Location ASTAssign::emit(Function& func) { const Def* def = _env->find(symbol_for(_dest)); - if (!def) return ssa_none(); - return func.add(new StoreInsn(def->location, _child->emit(func), false)); + if (!def) return loc_none(); + return func.add(new StoreInsn(def->location, _child->emit(func))); } void ASTAssign::format(stream& io) const { - write(io, "(= ", symbol_for(_dest), " ", _child, ")"); + write(io, "(= ", symbol_for(_dest), " ", _child, ")"); + } + + ASTAnnotate::ASTAnnotate(SourceLocation loc, ASTNode* value, const Type* type): + ASTNode(loc), _value(value), _type(type) { + _value->inc(); + } + + ASTAnnotate::~ASTAnnotate() { + _value->dec(); + } + + const Type* ASTAnnotate::lazy_type() { + const Type* inferred = unify(_value->type(), _type); + if (!inferred) { + err(_value->loc(), "Could not assign type '", _type, + "' to value of incompatible type '", _value->type(), "'."); + return ERROR; + } + return inferred; + } + + ref ASTAnnotate::emit(ref& parent) { + return _value->emit(parent); + } + + Location ASTAnnotate::emit(Function& function) { + return _value->emit(function); + } + + void ASTAnnotate::format(stream& io) const { + write(io, "(: ", _value, " ", _type, ")"); } } diff --git a/ast.h b/compiler/ast.h similarity index 74% rename from ast.h rename to compiler/ast.h index cc2b88f..b116b84 100644 --- a/ast.h +++ b/compiler/ast.h @@ -9,6 +9,7 @@ #include "type.h" #include "values.h" #include "ssa.h" +#include "ir.h" namespace basil { class Def; @@ -25,6 +26,7 @@ namespace basil { SourceLocation loc() const; const Type* type(); + virtual ref emit(ref& parent) = 0; virtual Location emit(Function& function) = 0; virtual void format(stream& io) const = 0; }; @@ -36,6 +38,7 @@ namespace basil { public: ASTSingleton(const Type* type); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -46,6 +49,7 @@ namespace basil { public: ASTVoid(SourceLocation loc); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -57,6 +61,7 @@ namespace basil { public: ASTInt(SourceLocation loc, i64 value); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -68,6 +73,7 @@ namespace basil { public: ASTSymbol(SourceLocation loc, u64 value); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -79,6 +85,7 @@ namespace basil { public: ASTString(SourceLocation, const string& value); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -90,6 +97,7 @@ namespace basil { public: ASTBool(SourceLocation loc, bool value); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -102,6 +110,18 @@ namespace basil { public: ASTVar(SourceLocation loc, const ref env, u64 name); + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTExtern : public ASTNode { + protected: + const Type* lazy_type() override; + public: + ASTExtern(SourceLocation loc); + + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -138,6 +158,7 @@ namespace basil { ASTBinaryMath(SourceLocation loc, ASTMathOp op, ASTNode* left, ASTNode* right); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -157,6 +178,7 @@ namespace basil { ASTBinaryLogic(SourceLocation loc, ASTLogicOp op, ASTNode* left, ASTNode* right); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -167,6 +189,7 @@ namespace basil { public: ASTNot(SourceLocation loc, ASTNode* child); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -184,6 +207,7 @@ namespace basil { ASTBinaryEqual(SourceLocation loc, ASTEqualOp op, ASTNode* left, ASTNode* right); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -203,6 +227,7 @@ namespace basil { ASTBinaryRel(SourceLocation loc, ASTRelOp op, ASTNode* left, ASTNode* right); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -215,6 +240,7 @@ namespace basil { public: ASTDefine(SourceLocation loc, ref env, u64 name, ASTNode* value); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -228,6 +254,7 @@ namespace basil { ASTCall(SourceLocation loc, ASTNode* func, const vector& args); ~ASTCall(); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -240,6 +267,7 @@ namespace basil { public: ASTIncompleteFn(SourceLocation loc, const Type* args, i64 name); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -252,6 +280,7 @@ namespace basil { i64 _name; bool _emitted; u32 _label; + ref _entry, _exit; protected: const Type* lazy_type() override; public: @@ -259,6 +288,7 @@ namespace basil { const vector& args, ASTNode* body, i64 name = -1); ~ASTFunction(); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -271,6 +301,7 @@ namespace basil { ASTBlock(SourceLocation loc, const vector& exprs); ~ASTBlock(); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -283,6 +314,7 @@ namespace basil { ASTIf(SourceLocation loc, ASTNode* cond, ASTNode* if_true, ASTNode* if_false); ~ASTIf(); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -295,6 +327,7 @@ namespace basil { ASTWhile(SourceLocation loc, ASTNode* cond, ASTNode* body); ~ASTWhile(); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -305,6 +338,7 @@ namespace basil { public: ASTIsEmpty(SourceLocation loc, ASTNode* list); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -315,6 +349,7 @@ namespace basil { public: ASTHead(SourceLocation loc, ASTNode* list); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -325,6 +360,7 @@ namespace basil { public: ASTTail(SourceLocation loc, ASTNode* list); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -335,6 +371,7 @@ namespace basil { public: ASTCons(SourceLocation loc, ASTNode* first, ASTNode* rest); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -345,6 +382,7 @@ namespace basil { public: ASTLength(SourceLocation loc, ASTNode* child); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; @@ -355,25 +393,28 @@ namespace basil { public: ASTDisplay(SourceLocation loc, ASTNode* node); + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; - class ASTNativeCall : public ASTNode { - const string _func_name; - const Type * _ret; - const vector _args; - const vector _arg_types; + class ASTNativeCall : public ASTNode { + const string _func_name; + const Type * _ret; + const vector _args; + const vector _arg_types; protected: - const Type* lazy_type() override; + const Type* lazy_type() override; public: - ASTNativeCall(SourceLocation loc, const string &func_name, const Type* ret); - // TODO make args a variadic template instead of a vector - ASTNativeCall(SourceLocation loc, const string &func_name, const Type *ret, const vector& args, const vector& arg_types); - ~ASTNativeCall(); - Location emit(Function& function) override; - void format(stream& io) const override; - }; + ASTNativeCall(SourceLocation loc, const string &func_name, const Type* ret); + // TODO make args a variadic template instead of a vector + ASTNativeCall(SourceLocation loc, const string &func_name, const Type *ret, const vector& args, const vector& arg_types); + ~ASTNativeCall(); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; class ASTAssign : public ASTUnary { ref _env; @@ -384,6 +425,21 @@ namespace basil { ASTAssign(SourceLocation loc, const ref env, u64 dest, ASTNode* src); + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTAnnotate : public ASTNode { + ASTNode* _value; + const Type* _type; + protected: + const Type* lazy_type() override; + public: + ASTAnnotate(SourceLocation loc, ASTNode* value, const Type* type); + ~ASTAnnotate(); + + ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; }; diff --git a/driver.cpp b/compiler/driver.cpp similarity index 71% rename from driver.cpp rename to compiler/driver.cpp index 1cf0244..d1f673d 100644 --- a/driver.cpp +++ b/compiler/driver.cpp @@ -5,7 +5,6 @@ #include "values.h" #include "native.h" #include "eval.h" -#include "ssa.h" #include "ast.h" #include "util/io.h" @@ -13,8 +12,9 @@ namespace basil { static bool _print_tokens = false, _print_parsed = false, _print_ast = false, - _print_ssa = false, - _print_asm = false; + _print_ir = false, + _print_asm = false, + _compile_only = false; void print_tokens(bool should) { _print_tokens = should; @@ -28,13 +28,21 @@ namespace basil { _print_ast = should; } - void print_ssa(bool should) { - _print_ssa = should; + void print_ir(bool should) { + _print_ir = should; } void print_asm(bool should) { _print_asm = should; } + + void compile_only(bool should) { + _compile_only = should; + } + + bool is_compile_only() { + return _compile_only; + } vector lex(Source::View& view) { vector tokens; @@ -65,14 +73,14 @@ namespace basil { ref create_global_env() { ref root = create_root_env(); - Env global_env(root); + ref global_env = newref(root); return global_env; } void compile(Value value, Object& object, Function& fn) { fn.allocate(); fn.emit(object); - ssa_emit_constants(object); + emit_constants(object); if (error_count()) return; if (_print_asm) { @@ -81,19 +89,15 @@ namespace basil { while (code.size()) printf("%02x ", code.read()); println(RESET, "\n"); } - - add_native_functions(object); - - object.load(); } void generate(Value value, Function& fn) { - Location last = ssa_none(); + Location last = loc_none(); if (value.is_runtime()) last = value.get_runtime()->emit(fn); - if (last.type != SSA_NONE) fn.add(new RetInsn(last)); + if (last.type != LOC_NONE) fn.add(new RetInsn(last)); if (error_count()) return; - if (_print_ssa) { + if (_print_ir) { print(BOLDMAGENTA); print(fn); println(RESET, "\n"); @@ -101,13 +105,13 @@ namespace basil { } void compile(Value value, Object& object) { - Function main_fn("main"); - Location last = ssa_none(); + Function main_fn("_start"); + Location last = loc_none(); if (value.is_runtime()) last = value.get_runtime()->emit(main_fn); - if (last.type != SSA_NONE) main_fn.add(new RetInsn(last)); + if (last.type != LOC_NONE) main_fn.add(new RetInsn(last)); if (error_count()) return; - if (_print_ssa) { + if (_print_ir) { print(BOLDMAGENTA); print(main_fn); println(RESET, "\n"); @@ -134,13 +138,13 @@ namespace basil { } int execute(Value value, const Object& object) { - auto main_jit = object.find(jasmine::global("main")); + auto main_jit = object.find(jasmine::global("_start")); if (main_jit) return ((i64(*)())main_jit)(); return 1; } Value repl(ref global, Source& src, Function& mainfn) { - print("? "); + print("? "); auto view = src.expand(_stdin); auto tokens = lex(view); if (error_count()) return print_errors(_stdout), error(); @@ -164,6 +168,8 @@ namespace basil { jasmine::Object object; generate(result, mainfn); compile(result, object, mainfn); + add_native_functions(object); + object.load(); if (error_count()) return print_errors(_stdout), error(); print(BOLDBLUE); @@ -175,17 +181,17 @@ namespace basil { ref load(Source& src) { auto view = src.begin(); auto tokens = lex(view); - if (error_count()) return print_errors(_stdout), ref::null(); + if (error_count()) return print_errors(_stdout), nullptr; TokenView tview(tokens, src); Value program = parse(tview); - if (error_count()) return print_errors(_stdout), ref::null(); + if (error_count()) return print_errors(_stdout), nullptr; ref global = create_global_env(); prep(global, program); Value result = eval(global, program); - if (error_count()) return print_errors(_stdout), ref::null(); + if (error_count()) return print_errors(_stdout), nullptr; return global; } @@ -211,8 +217,46 @@ namespace basil { jasmine::Object object; compile(result, object); + add_native_functions(object); + object.load(); if (error_count()) return print_errors(_stdout), 1; return execute(result, object); } + + string change_ending(string s, string e) { + string d = ""; + int last = 0; + for (int i = 0; i < s.size(); i ++) if (s[i] == '.') last = i; + for (int i = 0; i < last; i ++) d += s[i]; + return d + e; + } + + int build(Source& src, const char* filename) { + string dest = change_ending(filename, ".o"); + auto view = src.begin(); + auto tokens = lex(view); + if (error_count()) return print_errors(_stdout), 1; + + TokenView tview(tokens, src); + Value program = parse(tview); + if (error_count()) return print_errors(_stdout), 1; + + ref global = create_global_env(); + + prep(global, program); + Value result = eval(global, program); + if (error_count()) return print_errors(_stdout), 1; + + if (!result.is_runtime()) return 0; + if (_print_ast) + println(BOLDCYAN, result.get_runtime(), RESET, "\n"); + + jasmine::Object object; + compile(result, object); + if (error_count()) return print_errors(_stdout), 1; + + object.writeELF((const char*)dest.raw()); + return 0; + } } \ No newline at end of file diff --git a/driver.h b/compiler/driver.h similarity index 71% rename from driver.h rename to compiler/driver.h index 095e67b..99e520e 100644 --- a/driver.h +++ b/compiler/driver.h @@ -3,19 +3,22 @@ #include "source.h" #include "env.h" -#include "ssa.h" +#include "ir.h" #include "values.h" namespace basil { void print_tokens(bool should); void print_parsed(bool should); void print_ast(bool should); - void print_ssa(bool should); + void print_ir(bool should); void print_asm(bool should); + void compile_only(bool should); + bool is_compile_only(); Value repl(ref global, Source& src, Function& mainfn); ref load(Source& src); int run(Source& src); + int build(Source& src, const char* dest); } #endif \ No newline at end of file diff --git a/env.cpp b/compiler/env.cpp similarity index 98% rename from env.cpp rename to compiler/env.cpp index dbc8e2f..9fcd078 100644 --- a/env.cpp +++ b/compiler/env.cpp @@ -104,8 +104,7 @@ namespace basil { } ref Env::clone() const { - Env new_env(_parent); - ref new_ref(new_env); + ref new_ref = newref(_parent); for (auto& p : _defs) new_ref->_defs.put(p.first, p.second); new_ref->_runtime = _runtime; return new_ref; diff --git a/env.h b/compiler/env.h similarity index 96% rename from env.h rename to compiler/env.h index e4519d2..d406edc 100644 --- a/env.h +++ b/compiler/env.h @@ -7,6 +7,7 @@ #include "util/rc.h" #include "values.h" #include "ssa.h" +#include "ir.h" namespace basil { struct Def { @@ -16,6 +17,7 @@ namespace basil { bool is_proc; // is the definition a scalar or procedure? u8 arity; // number of arguments taken by a procedure. u8 precedence; // precedence of infix procedure + SSAIdent ident; Location location; Def(bool is_macro_in = false, bool is_procedure_in = false, @@ -34,7 +36,7 @@ namespace basil { ref _parent; bool _runtime; public: - Env(const ref& parent = ref::null()); + Env(const ref& parent = nullptr); void def(const string& name); void def(const string& name, u8 arity); diff --git a/errors.cpp b/compiler/errors.cpp similarity index 100% rename from errors.cpp rename to compiler/errors.cpp diff --git a/errors.h b/compiler/errors.h similarity index 100% rename from errors.h rename to compiler/errors.h diff --git a/eval.cpp b/compiler/eval.cpp similarity index 82% rename from eval.cpp rename to compiler/eval.cpp index abedaca..fa5f627 100644 --- a/eval.cpp +++ b/compiler/eval.cpp @@ -113,8 +113,16 @@ namespace basil { return length(args.get_product()[0]); } - Value builtin_char_at(ref env, const Value& args) { - return char_at(args.get_product()[0], args.get_product()[1]); + Value builtin_at(ref env, const Value& args) { + return at(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_strcat(ref env, const Value& args) { + return strcat(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_substr(ref env, const Value& args) { + return substr(args.get_product()[0], args.get_product()[1], args.get_product()[2]); } Value builtin_if_macro(ref env, const Value& args) { @@ -147,8 +155,16 @@ namespace basil { else return eval(env, args.get_product()[2]); } + Value builtin_annotate(ref env, const Value& args) { + return annotate(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_typeof(ref env, const Value& args) { + return type_of(args.get_product()[0]); + } + ref create_root_env() { - ref root; + ref root = newref(); root->def("nil", Value(VOID)); root->infix("+", new FunctionValue(root, builtin_add, 2), 2, 20); root->infix("-", new FunctionValue(root, builtin_sub, 2), 2, 20); @@ -179,9 +195,18 @@ namespace basil { root->def("read-word", new FunctionValue(root, builtin_read_word, 0), 0); root->def("read-int", new FunctionValue(root, builtin_read_int, 0), 0); root->infix("length", new FunctionValue(root, builtin_length, 1), 1, 50); - root->infix("at", new FunctionValue(root, builtin_char_at, 2), 2, 90); + root->infix("at", new FunctionValue(root, builtin_at, 2), 2, 90); + root->infix("^", new FunctionValue(root, builtin_strcat, 2), 2, 20); + root->infix("substr", new FunctionValue(root, builtin_substr, 3), 3, 90); + root->def("annotate", new FunctionValue(root, builtin_annotate, 2), 2); + root->def("typeof", new FunctionValue(root, builtin_typeof, 1), 1); + root->def("true", Value(true, BOOL)); root->def("false", Value(false, BOOL)); + root->def("extern", Value(new ASTExtern({}))); + root->def("int", Value(INT, TYPE)); + root->def("symbol", Value(SYMBOL, TYPE)); + root->def("string", Value(STRING, TYPE)); return root; } @@ -214,20 +239,20 @@ namespace basil { return values; } - bool introduces_env(const Value& list) { - if (!list.is_list()) return false; - const Value& h = head(list); - if (!h.is_symbol()) return false; - const string& name = symbol_for(h.get_symbol()); - if (name == "def") { - return tail(list).is_list() && - head(tail(list)).is_list(); // is procedure - } - else if (name == "lambda" || name == "infix" - || name == "infix-macro" || name == "macro") return true; + bool introduces_env(const Value& list) { + if (!list.is_list()) return false; + const Value& h = head(list); + if (!h.is_symbol()) return false; + const string& name = symbol_for(h.get_symbol()); + if (name == "def") { + return tail(list).is_list() && + head(tail(list)).is_list(); // is procedure + } + else if (name == "lambda" || name == "infix" + || name == "infix-macro" || name == "macro") return true; - return false; - } + return false; + } static i64 traverse_deep = 0; @@ -325,32 +350,84 @@ namespace basil { else traverse_list(env, item, visit_macro_defs); } + bool symbol_matches(const Value& term, const string& sym) { + return term.is_symbol() && term.get_symbol() == symbol_value(sym); + } + + bool is_annotation(const Value& term) { + return term.is_list() && symbol_matches(head(term), "annotate"); + } + + Value annotation_type(const Value& term) { + return head(tail(tail(term))); + } + + bool is_valid_argument(const Value& term) { + return term.is_symbol() // name + || is_annotation(term) && tail(term).is_list() && head(tail(term)).is_symbol(); + } + + bool is_valid_def(const Value& term) { + return is_valid_argument(term) + || term.is_list() && head(term).is_symbol() + || is_annotation(term) && tail(term).is_list() && is_valid_def(head(tail(term))); + } + + bool is_valid_infix_def(const Value& term) { + return term.is_list() && tail(term).is_list() && head(tail(term)).is_symbol() + || is_annotation(term) && tail(term).is_list() && is_valid_infix_def(head(tail(term))); + } + + string get_arg_name(const Value& term) { + return is_annotation(term) ? get_arg_name(head(tail(term))) + : symbol_for(term.get_symbol()); + } + + string get_def_name(const Value& term) { + if (term.is_symbol()) return symbol_for(term.get_symbol()); + else if (is_annotation(term)) return get_def_name(head(tail(term))); + else return symbol_for(head(term).get_symbol()); + } + + Value get_def_info(const Value& term) { + if (is_annotation(term)) return get_def_info(head(tail(term))); + return term; + } + + string get_infix_name(const Value& term) { + if (is_annotation(term)) return get_infix_name(head(tail(term))); + return symbol_for(head(tail(term)).get_symbol()); + } + void visit_defs(ref env, const Value& item) { if (!item.is_list()) return; Value h = head(item); - if (h.is_symbol() && - (h.get_symbol() == symbol_value("def") - || h.get_symbol() == symbol_value("infix"))) { - bool infix = h.get_symbol() == symbol_value("infix"); + if (symbol_matches(h, "def") || symbol_matches(h, "infix")) { + bool infix = symbol_matches(h, "infix"); u8 precedence = 0; vector values = to_vector(item); u32 i = 1; if (values.size() >= 2 && infix && values[i].is_int()) { - precedence = u8(values[i].get_int()); + precedence = u8(values[i].get_int()); // consume precedence i ++; } if (i + 1 >= values.size() || - (!values[i].is_symbol() && - !(values[i].is_list() && head(values[i]).is_symbol()) && - !(values[i].is_list() && tail(values[i]).is_list() && - head(tail(values[i])).is_symbol()))) { + !(infix ? is_valid_infix_def(values[i]) : is_valid_def(values[i]))) { err(item.loc(), "Expected variable or function name ", "in definition."); return; } if (values.size() < 3) err(item.loc(), "Expected value in definition."); - if (values[i].is_list()) { // procedure + if (is_valid_argument(values[i])) { // variable + string name = get_arg_name(values[i]); + // if (env->find(name)) { + // err(values[i].loc(), "Redefinition of '", name, "'."); + // return; + // } + env->def(name); + } + else { // procedure if (infix) { Value rest = tail(values[i]); if (!rest.is_list()) { @@ -358,8 +435,8 @@ namespace basil { "argument."); return; } - const string& name = symbol_for(head(rest).get_symbol()); - // if (env->find(name)) { + string name = get_infix_name(values[i]); + // if (env->find(name)) { // err(values[i].loc(), "Redefinition of '", name, "'."); // return; // } @@ -367,7 +444,7 @@ namespace basil { precedence); } else { - const string& name = symbol_for(head(values[i]).get_symbol()); + const string& name = get_def_name(values[i]); // if (env->find(name)) { // err(values[i].loc(), "Redefinition of '", name, "'."); // return; @@ -375,14 +452,6 @@ namespace basil { env->def(name, to_vector(tail(values[i])).size()); } } - else { - const string& name = symbol_for(values[i].get_symbol()); - // if (env->find(name)) { - // err(values[i].loc(), "Redefinition of '", name, "'."); - // return; - // } - env->def(name); - } } else traverse_list(env, item, visit_defs); } @@ -639,6 +708,11 @@ namespace basil { visit_defs(env, term); } + bool is_keyword(const Value& term) { + return term.is_list() && tail(term).is_list() && tail(tail(term)).is_void() + && symbol_matches(head(term), "quote") && symbol_matches(head(tail(term)), "quote"); + } + // definition stuff Value define(ref env, const Value& term, bool is_macro) { vector values = to_vector(term); @@ -647,10 +721,14 @@ namespace basil { // don't need to check the number of values or their types // exhaustively. - if (values[1].is_symbol()) { // variable - const string& name = symbol_for(values[1].get_symbol()); + if (is_valid_argument(values[1])) { // variable + string name = get_arg_name(values[1]); if (is_macro) { + if (is_annotation(values[1])) { + err(values[1].loc(), "Type annotations are forbidden in macro definitions."); + return error(); + } env->def_macro(name, Value(new AliasValue(values[2]))); return Value(VOID); } @@ -658,6 +736,8 @@ namespace basil { Value init = eval(env, values[2]); if (env->is_runtime()) init = lower(init); + if (is_annotation(values[1])) + init = annotate(init, eval(env, annotation_type(values[1]))); env->def(name, init); if (init.is_runtime()) return new ASTDefine(values[0].loc(), env, @@ -666,39 +746,67 @@ namespace basil { } } else if (values[1].is_list()) { // procedure - const string& name = symbol_for(head(values[1]).get_symbol()); - - Env env_descendant(env); - ref function_env(env_descendant); - vector args = to_vector(tail(values[1])); + string name = get_def_name(values[1]); + + ref function_env = newref(env); + vector args = to_vector(tail(get_def_info(values[1]))); vector argnames; + vector body; + vector argts; for (const Value& v : args) { - if (v.is_symbol()) { - argnames.push(v.get_symbol()); - function_env->def(symbol_for(v.get_symbol())); + if (is_valid_argument(v)) { + string name = get_arg_name(v); + argnames.push(symbol_value(name)); + function_env->def(name); } - else if (v.is_list() && head(v).is_symbol() - && symbol_for(head(v).get_symbol()) == "quote") { + else if (is_keyword(v)) { argnames.push(eval(env, v).get_symbol() | KEYWORD_ARG_BIT); } else { - err(v.loc(), "Only symbols and quoted symbols are permitted ", - "within an argument list."); + err(v.loc(), "Only symbols, annotated symbols, and quoted symbols " + "are permitted within an argument list; given '", v, "'."); return error(); } + if (is_annotation(v)) { + if (is_macro) { + err(v.loc(), "Type annotations are forbidden in macro definitions."); + return error(); + } + Value tval = eval(env, annotation_type(v)); + if (!tval.is_type()) { + err(v.loc(), "Expected type value in annotation, given '", tval.type(), "'."); + return error(); + } + body.push(v); + body.push(list_of(Value("list-of"))); + argts.push(tval.get_type()); + } + else argts.push(find()); } - vector body; for (u32 i = 2; i < values.size(); i ++) body.push(values[i]); Value body_term = cons(Value("do"), list_of(body)); if (is_macro) { + if (is_annotation(values[1])) { + err(values[1].loc(), "Type annotations are forbidden in macro definitions."); + return error(); + } Value mac(new MacroValue(function_env, argnames, body_term)); env->def_macro(name, mac, argnames.size()); } else { - prep(function_env, body_term); Value func(new FunctionValue(function_env, argnames, body_term, symbol_value(name))); + if (is_annotation(values[1])) { + Value tval = eval(env, annotation_type(values[1])); + if (!tval.is_type()) { + err(values[1].loc(), "Expected type value in annotation, given '", tval.type(), "'."); + return error(); + } + func = annotate(func, Value(find(find(argts), tval.get_type()), TYPE)); + } env->def(name, func, argnames.size()); + if (find(argts)->concrete()) + instantiate(func.loc(), func.get_function(), find(argts)); } return Value(VOID); } @@ -749,8 +857,7 @@ namespace basil { return error(); } i ++; - Env env_descendant(env); - ref function_env(env_descendant); + ref function_env = newref(env); vector argnames; for (const Value& v : args) { if (v.is_symbol()) { @@ -797,8 +904,7 @@ namespace basil { "expression."); return error(); } - Env env_descendant(env); - ref function_env(env_descendant); + ref function_env = newref(env); vector args = to_vector(values[1]); vector argnames; for (const Value& v : args) { @@ -979,6 +1085,30 @@ namespace basil { return list_of(vals); } + Value tuple_of(ref env, const Value& term) { + Value items = tail(term); + + const Value* v = &items; + vector vals; + while (v->is_list()) { + vals.push(eval(env, v->get_list().head())); + v = &v->get_list().tail(); + } + return tuple_of(vals); + } + + Value array_of(ref env, const Value& term) { + Value items = tail(term); + + const Value* v = &items; + vector vals; + while (v->is_list()) { + vals.push(eval(env, v->get_list().head())); + v = &v->get_list().tail(); + } + return array_of(vals); + } + struct CompilationUnit { Source source; ref env; @@ -1038,11 +1168,19 @@ namespace basil { else if (name == "do") return do_block(env, term); else if (name == "if") return eval(env, if_expr(env, term)); else if (name == "list-of") return list_of(env, term); + else if (name == "tuple-of") return tuple_of(env, term); + else if (name == "array-of") return array_of(env, term); else if (name == "use") return use(env, term); else if (name == "while") return while_stmt(env, term); } Value first = eval(env, h); + + if (h.is_symbol() && tail(term).is_void()) { + const Def* def = env->find(symbol_for(h.get_symbol())); + if (def->is_infix) return first; + } + if (first.is_macro()) { vector args; const Value* v = &term.get_list().tail(); diff --git a/eval.h b/compiler/eval.h similarity index 100% rename from eval.h rename to compiler/eval.h diff --git a/compiler/ir.cpp b/compiler/ir.cpp new file mode 100644 index 0000000..ebca315 --- /dev/null +++ b/compiler/ir.cpp @@ -0,0 +1,802 @@ +#include "ir.h" +#include "util/hash.h" +#include "ops.h" + +namespace basil { + using namespace x64; + + Location loc_none() { + Location loc; + loc.type = LOC_NONE; + return loc; + } + + Location loc_immediate(i64 i) { + Location loc; + loc.type = LOC_IMMEDIATE; + loc.immediate = i; + return loc; + } + + Location loc_label(const string& label) { + Location loc; + loc.type = LOC_LABEL; + loc.label_index = find_label(label); + return loc; + } + + Insn::Insn(InsnType kind): + _kind(kind), _loc(loc_none()), _func(nullptr) {} + + Insn::~Insn() { + // + } + + InsnType Insn::kind() const { + return _kind; + } + + void Insn::setfunc(Function* func) { + _func = func; + } + + const Location& Insn::loc() const { + return _loc; + } + + Location& Insn::loc() { + if (_loc.type == LOC_NONE) _loc = lazy_loc(); + return _loc; + } + + vector& Insn::succ() { + return _succ; + } + + const vector& Insn::succ() const { + return _succ; + } + + set& Insn::in() { + return _in; + } + + set& Insn::out() { + return _out; + } + + const set& Insn::in() const { + return _in; + } + + const set& Insn::out() const { + return _out; + } + + static u32 anonymous_locals = 0; + static u32 anonymous_labels = 0; + static vector all_labels; + static map label_map; + static vector all_locals; + static vector all_constants; + + u32 find_label(const string& label) { + auto it = label_map.find(label); + if (it == label_map.end()) return add_label(label); + return it->second; + } + + u32 add_label(const string& label) { + all_labels.push(label); + label_map[label] = all_labels.size() - 1; + return all_labels.size() - 1; + } + + u32 next_label() { + buffer b; + write(b, ".L", anonymous_labels ++); + string s; + read(b, s); + all_labels.push(s); + label_map[s] = all_labels.size() - 1; + return all_labels.size() - 1; + } + + Location next_local(const Type* t) { + buffer b; + write(b, ".t", anonymous_locals ++); + string s; + read(b, s); + all_locals.push({ s, 0, t, -1, 0 }); + + Location loc; + loc.type = LOC_LOCAL; + loc.local_index = all_locals.size() - 1; + return loc; + } + + Location const_loc(u32 label, const string& constant) { + ConstantInfo info; + info.type = STRING; + info.name = all_labels[label]; + for (u32 i = 0; i < constant.size(); i ++) info.data.push(constant[i]); + info.data.push('\0'); + + all_constants.push(info); + Location loc; + loc.type = LOC_CONSTANT; + loc.constant_index = all_constants.size() - 1; + return loc; + } + + Symbol symbol_for_label(u32 label, SymbolLinkage type) { + return type == GLOBAL_SYMBOL ? + global((const char*)all_labels[label].raw()) + : local((const char*)all_labels[label].raw()); + } + + void emit_constants(Object& object) { + using namespace x64; + writeto(object); + for (const ConstantInfo& info : all_constants) { + global_label(info.name); + for (u8 b : info.data) object.code().write(b); + } + } + + const Type* ssa_type(const Location& loc) { + switch (loc.type) { + case LOC_NONE: + return VOID; + case LOC_LOCAL: + return all_locals[loc.local_index].type; + case LOC_CONSTANT: + return all_constants[loc.constant_index].type; + case LOC_IMMEDIATE: + return INT; // close enough at this stage + case LOC_LABEL: + return INT; // ...close enough :p + case LOC_REGISTER: + return INT; + } + } + + const u8 BINARY_INSN = 128; // BINARY_INSN kind flag + + i64 immediate_of(const Location& loc) { + return loc.immediate; + } + + const string& label_of(const Location& loc) { + return all_labels[loc.label_index]; + } + + LocalInfo& local_of(const Location& loc) { + return all_locals[loc.local_index]; + } + + ConstantInfo& constant_of(const Location& loc) { + return all_constants[loc.constant_index]; + } + + Location loc_register(u32 reg) { + Location loc; + loc.type = LOC_REGISTER; + loc.reg = reg; + return loc; + } + + void Function::liveness() { + bool changed = true; + while (changed) { + changed = false; + for (i64 i = _insns.size() - 1; i >= 0; i --) { + Insn* in = _insns[i]; + u32 initial_in = in->in().size(), initial_out = in->out().size(); + for (const Insn* succ : in->succ()) { + for (u32 l : succ->in()) in->out().insert(l); + } + for (u32 l : in->out()) in->in().insert(l); + in->liveout(); + if (in->in().size() != initial_in || in->out().size() != initial_out) + changed = true; + } + } + + // for (i64 i = 0; i < _insns.size(); i ++) { + // print(_insns[i], " { "); + // for (auto u : _insns[i]->in()) print(all_locals[u].name, " "); + // print("} -> { "); + // for (auto u : _insns[i]->out()) print(all_locals[u].name, " "); + // println("}"); + // } + } + + void Function::to_registers() { + map> ranges; + for (i64 i = 0; i < _insns.size(); i ++) { + for (u32 l : _insns[i]->out()) { + if (_insns[i]->in().find(l) == _insns[i]->in().end()) + ranges.put(l, { i, -1 }); + } + for (u32 l : _insns[i]->in()) { + if (_insns[i]->out().find(l) == _insns[i]->out().end()) + ranges[l].second = i; + } + } + + // for (auto& p : ranges) { + // println(all_locals[p.first].name, " live between ", p.second.first, " and ", p.second.second); + // } + + vector> gens, kills; + for (i64 i = 0; i < _insns.size(); i ++) + gens.push({}), kills.push({}); + for (auto& p : ranges) { + gens[p.second.first].push(p.first); + kills[p.second.second].push(p.first); + } + + // for (i64 i = 0; i < _insns.size(); i ++) { + // print(_insns[i], ": "); + // for (u32 l : gens[i]) print("GEN-", all_locals[l].name, " "); + // for (u32 l : kills[i]) print("KILL-", all_locals[l].name, " "); + // println(""); + // } + + vector reg_stack = allocatable_registers(); + for (i64 i = 0; i < _insns.size(); i ++) { + for (u32 g : gens[i]) { + if (all_locals[g].reg >= 0) + ; // + else if (reg_stack.size() == 0) + all_locals[g].offset = -(_stack += 8); // spill + else + all_locals[g].reg = reg_stack.back(), reg_stack.pop(); + } + for (u32 k : kills[i]) { + if (all_locals[k].reg != -1) + reg_stack.push(all_locals[k].reg); + } + } + } + + Function::Function(u32 label): + _stack(0), _label(label), _end(next_label()) { + _ret = loc_register(RAX); + } + + Function::Function(const string& label): + Function(add_label(label)) {} + + Location Function::create_local(const Type* t) { + Location l = basil::next_local(t); + _locals.push(l); + return l; + } + + Function::~Function() { + for (Insn* i : _insns) delete i; + for (Function* f : _fns) delete f; + } + + void Function::place_label(u32 label) { + _labels[label] = _insns.size(); + } + + Function& Function::create_function() { + _fns.push(new Function(next_label())); + return *_fns.back(); + } + + Function& Function::create_function(const string& name) { + _fns.push(new Function(name)); + return *_fns.back(); + } + + Location Function::create_local(const string& name, const Type* t) { + LocalInfo info = { name, 0, t, -1, 0 }; + all_locals.push(info); + + Location loc; + loc.type = LOC_LOCAL; + loc.local_index = all_locals.size() - 1; + _locals.push(loc); + return loc; + } + + Location Function::add(Insn* insn) { + insn->setfunc(this); + if (_insns.size() > 0) + _insns.back()->succ().push(insn); + _insns.push(insn); + return insn->loc(); + } + + u32 Function::label() const { + return _label; + } + + void Function::allocate() { + for (Function* fn : _fns) fn->allocate(); + liveness(); + to_registers(); + } + + void Function::emit(Object& obj) { + for (Function* fn : _fns) fn->emit(obj); + + writeto(obj); + global_label(all_labels[_label]); + open_frame(_stack); + for (Insn* i : _insns) i->emit(); + local_label(all_labels[_end]); + close_frame(_stack); + } + + void Function::format(stream& io) const { + for (Function* fn : _fns) fn->format(io); + writeln(io, all_labels[_label], ":"); + for (Insn* i : _insns) writeln(io, " ", i); + } + + u32 Function::end_label() const { + return _end; + } + + const Location& Function::ret_loc() const { + return _ret; + } + + Insn* Function::last() const { + if (_insns.size() == 0) return nullptr; + return _insns.back(); + } + + Location LoadInsn::lazy_loc() { + return _func->create_local(ssa_type(_src)); + } + + LoadInsn::LoadInsn(Location src): + Insn(LOAD_INSN), _src(src) {} + + void LoadInsn::emit() { + move(_loc, _src); + } + + void LoadInsn::format(stream& io) const { + write(io, _loc, " = ", _src); + } + + void LoadInsn::liveout() { + if (_src.type == LOC_LOCAL) in().insert(_src.local_index); + if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); + } + + Location StoreInsn::lazy_loc() { + return _dest; + } + + StoreInsn::StoreInsn(Location dest, Location src): + Insn(STORE_INSN), _dest(dest), _src(src) {} + + void StoreInsn::emit() { + move(_dest, _src); + } + + void StoreInsn::format(stream& io) const { + write(io, _loc, " = ", _src); + } + + void StoreInsn::liveout() { + if (_src.type == LOC_LOCAL) in().insert(_src.local_index); + if (_dest.type == LOC_LOCAL) in().erase(_dest.local_index); + } + + LoadPtrInsn::LoadPtrInsn(Location src, const Type* t, i32 offset): + Insn(LOAD_PTR_INSN), _src(src), _type(t), _offset(offset) {} + + Location LoadPtrInsn::lazy_loc() { + return _func->create_local(_type); + } + + void LoadPtrInsn::emit() { + load(_loc, _src, _offset); + } + + void LoadPtrInsn::format(stream& io) const { + write(io, _loc, " = *", _src); + } + + void LoadPtrInsn::liveout() { + if (_src.type == LOC_LOCAL) in().insert(_src.local_index); + if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); + } + + StorePtrInsn::StorePtrInsn(Location dest, Location src, i32 offset): + Insn(STORE_PTR_INSN), _dest(dest), _src(src), _offset(offset) {} + + Location StorePtrInsn::lazy_loc() { + return loc_none(); + } + + void StorePtrInsn::emit() { + store(_dest, _src, _offset); + } + + void StorePtrInsn::format(stream& io) const { + write(io, "*", _dest, " = ", _src); + } + + void StorePtrInsn::liveout() { + if (_src.type == LOC_LOCAL) in().insert(_src.local_index); + if (_dest.type == LOC_LOCAL) in().insert(_dest.local_index); + } + + AddressInsn::AddressInsn(Location src, const Type* t): + Insn(ADDRESS_INSN), _src(src), _type(t) {} + + Location AddressInsn::lazy_loc() { + return _func->create_local(_type); + } + + void AddressInsn::emit() { + lea(_loc, _src); + } + + void AddressInsn::format(stream& io) const { + write(io, _loc, " = &", _src); + } + + void AddressInsn::liveout() { + if (_src.type == LOC_LOCAL) in().insert(_src.local_index); + if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); + } + + BinaryInsn::BinaryInsn(InsnType kind, const char* name, Location left, + Location right): + Insn(kind), _name(name), _left(left), _right(right) {} + + void BinaryInsn::format(stream& io) const { + write(io, _loc, " = ", _left, " ", _name, " ", _right); + } + + void BinaryInsn::liveout() { + if (_left.type == LOC_LOCAL) in().insert(_left.local_index); + if (_right.type == LOC_LOCAL) in().insert(_right.local_index); + if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); + } + + Location BinaryMathInsn::lazy_loc() { + return _func->create_local(ssa_type(_left)); + } + + BinaryMathInsn::BinaryMathInsn(InsnType kind, const char* name, Location left, + Location right): + BinaryInsn(kind, name, left, right) {} + + AddInsn::AddInsn(Location left, Location right): + BinaryMathInsn(ADD_INSN, "+", left, right) {} + + void AddInsn::emit() { + add(_loc, _left, _right); + } + + SubInsn::SubInsn(Location left, Location right): + BinaryMathInsn(SUB_INSN, "-", left, right) {} + + void SubInsn::emit() { + sub(_loc, _left, _right); + } + + MulInsn::MulInsn(Location left, Location right): + BinaryMathInsn(MUL_INSN, "*", left, right) {} + + void MulInsn::emit() { + mul(_loc, _left, _right); + } + + DivInsn::DivInsn(Location left, Location right): + BinaryMathInsn(DIV_INSN, "/", left, right) {} + + void DivInsn::emit() { + div(_loc, _left, _right); + } + + RemInsn::RemInsn(Location left, Location right): + BinaryMathInsn(REM_INSN, "%", left, right) {} + + void RemInsn::emit() { + rem(_loc, _left, _right); + } + + BinaryLogicInsn::BinaryLogicInsn(InsnType kind, const char* name, Location left, + Location right): + BinaryInsn(kind, name, left, right) {} + + Location BinaryLogicInsn::lazy_loc() { + return _func->create_local(BOOL); + } + + AndInsn::AndInsn(Location left, Location right): + BinaryLogicInsn(AND_INSN, "and", left, right) {} + + void AndInsn::emit() { + and_op(_loc, _left, _right); + } + + OrInsn::OrInsn(Location left, Location right): + BinaryLogicInsn(OR_INSN, "or", left, right) {} + + void OrInsn::emit() { + or_op(_loc, _left, _right); + } + + XorInsn::XorInsn(Location left, Location right): + BinaryLogicInsn(XOR_INSN, "xor", left, right) {} + + void XorInsn::emit() { + xor_op(_loc, _left, _right); + } + + NotInsn::NotInsn(Location src): + Insn(NOT_INSN), _src(src) {} + + Location NotInsn::lazy_loc() { + return _func->create_local(BOOL); + } + + void NotInsn::emit() { + not_op(_loc, _src); + } + + void NotInsn::format(stream& io) const { + write(io, _loc, " = ", "not ", _src); + } + + void NotInsn::liveout() { + if (_src.type == LOC_LOCAL) in().insert(_src.local_index); + if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); + } + + BinaryEqualityInsn::BinaryEqualityInsn(InsnType kind, const char* name, + Location left, Location right): + BinaryInsn(kind, name, left, right) {} + + Location BinaryEqualityInsn::lazy_loc() { + return _func->create_local(BOOL); + } + + EqualInsn::EqualInsn(Location left, Location right): + BinaryEqualityInsn(EQ_INSN, "==", left, right) {} + + void EqualInsn::emit() { + equal(_loc, _left, _right); + } + + InequalInsn::InequalInsn(Location left, Location right): + BinaryEqualityInsn(NOT_EQ_INSN, "!=", left, right) {} + + void InequalInsn::emit() { + not_equal(_loc, _left, _right); + } + + BinaryRelationInsn::BinaryRelationInsn(InsnType kind, const char* name, + Location left, Location right): + BinaryInsn(kind, name, left, right) {} + + Location BinaryRelationInsn::lazy_loc() { + return _func->create_local(BOOL); + } + + LessInsn::LessInsn(Location left, Location right): + BinaryRelationInsn(LESS_INSN, "<", left, right) {} + + void LessInsn::emit() { + less(_loc, _left, _right); + } + + LessEqualInsn::LessEqualInsn(Location left, Location right): + BinaryRelationInsn(LESS_EQ_INSN, "<=", left, right) {} + + void LessEqualInsn::emit() { + less_equal(_loc, _left, _right); + } + + GreaterInsn::GreaterInsn(Location left, Location right): + BinaryRelationInsn(GREATER_INSN, ">", left, right) {} + + void GreaterInsn::emit() { + greater(_loc, _left, _right); + } + + GreaterEqualInsn::GreaterEqualInsn(Location left, Location right): + BinaryRelationInsn(GREATER_EQ_INSN, ">=", left, right) {} + + void GreaterEqualInsn::emit() { + greater_equal(_loc, _left, _right); + } + + Location RetInsn::lazy_loc() { + return loc_none(); + } + + RetInsn::RetInsn(Location src): + Insn(RET_INSN), _src(src) {} + + void RetInsn::emit() { + move(_func->ret_loc(), _src); + if (this != _func->last()) { + Location loc; + loc.type = LOC_LABEL; + loc.label_index = _func->end_label(); + jump(loc); + } + } + + void RetInsn::format(stream& io) const { + write(io, "return ", _src); + } + + void RetInsn::liveout() { + while (in().size()) in().erase(*in().begin()); // all other variables die here + while (out().size()) out().erase(*out().begin()); // all other variables die here + if (_src.type == LOC_LOCAL) + in().insert(_src.local_index); + } + + LoadArgumentInsn::LoadArgumentInsn(u32 index, const Type* type): + Insn(LOAD_ARG_INSN), _index(index), _type(type) {} + + Location LoadArgumentInsn::lazy_loc() { + Location l = _func->create_local(_type); + return l; + } + + void LoadArgumentInsn::emit() { + get_arg(_loc, _index); + } + + void LoadArgumentInsn::format(stream& io) const { + write(io, _loc, " = $", _index); + } + + void LoadArgumentInsn::liveout() { + if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); + } + + CallInsn::CallInsn(Location fn, const vector& args, const Type* ret): + Insn(CALL_INSN), _fn(fn), _args(args), _ret(ret) {} + + Location CallInsn::lazy_loc() { + return _func->create_local(_ret); + } + + void CallInsn::emit() { + vector saved; + for (u32 i : in()) { + if (out().find(i) != out().end() && all_locals[i].reg >= 0) { + Location loc; + loc.type = LOC_LOCAL; + loc.local_index = i; + saved.push(loc); + } + } + for (u32 i = 0; i < saved.size(); i ++) push(saved[i]); + for (u32 i = 0; i < _args.size(); i ++) + set_arg(i, _args[i]); + call(_loc, _fn); + for (i64 i = i64(saved.size()) - 1; i >= 0; i --) pop(saved[i]); + } + + void CallInsn::format(stream& io) const { + write(io, _loc, " = ", _fn, "()"); + } + + void CallInsn::liveout() { + for (const Location& loc : _args) if (loc.type == LOC_LOCAL) + in().insert(loc.local_index); + if (_fn.type == LOC_LOCAL) in().insert(_fn.local_index); + if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); + } + + Label::Label(u32 label): + Insn(LABEL), _label(label) {} + + Location Label::lazy_loc() { + return loc_none(); + } + + void Label::emit() { + local_label(all_labels[_label]); + } + + void Label::format(stream& io) const { + write(io, "\b\b\b\b", all_labels[_label], ":"); + } + + void Label::liveout() { + // + } + + GotoInsn::GotoInsn(u32 label): + Insn(GOTO_INSN), _label(label) {} + + Location GotoInsn::lazy_loc() { + return loc_none(); + } + + void GotoInsn::emit() { + jmp(label64(symbol_for_label(_label, LOCAL_SYMBOL))); + } + + void GotoInsn::format(stream& io) const { + write(io, "goto ", all_labels[_label]); + } + + void GotoInsn::liveout() { + // + } + + IfZeroInsn::IfZeroInsn(u32 label, Location cond): + Insn(IFZERO_INSN), _label(label), _cond(cond) {} + + Location IfZeroInsn::lazy_loc() { + return loc_none(); + } + + void IfZeroInsn::emit() { + Location label; + label.type = LOC_LABEL; + label.label_index = _label; + jump_if_zero(label, _cond); + } + + void IfZeroInsn::format(stream& io) const { + write(io, "if not ", _cond, " goto ", all_labels[_label]); + } + + void IfZeroInsn::liveout() { + if (_cond.type == LOC_LOCAL) in().insert(_cond.local_index); + } +} + +void write(stream& io, const basil::Location& loc) { + switch (loc.type) { + case basil::LOC_NONE: + write(io, "none"); + return; + case basil::LOC_LOCAL: + write(io, basil::all_locals[loc.local_index].name); + if (basil::all_locals[loc.local_index].index > 0 + || basil::all_locals[loc.local_index].name[0] != '.') + write(io, ".", basil::all_locals[loc.local_index].index); + return; + case basil::LOC_IMMEDIATE: + write(io, loc.immediate); + return; + case basil::LOC_LABEL: + write(io, basil::all_labels[loc.label_index]); + return; + case basil::LOC_CONSTANT: + write(io, basil::all_constants[loc.constant_index].name); + return; + case basil::LOC_REGISTER: + write(io, "r", loc.reg); + return; + default: + return; + } +} + +void write(stream& io, basil::Insn* insn) { + insn->format(io); +} + +void write(stream& io, const basil::Insn* insn) { + insn->format(io); +} + +void write(stream& io, const basil::Function& func) { + func.format(io); +} \ No newline at end of file diff --git a/ssa.h b/compiler/ir.h similarity index 71% rename from ssa.h rename to compiler/ir.h index e73d0a6..4882ce7 100644 --- a/ssa.h +++ b/compiler/ir.h @@ -1,5 +1,5 @@ -#ifndef BASIL_SSA_H -#define BASIL_SSA_H +#ifndef BASIL_IR_H +#define BASIL_IR_H #include "util/defs.h" #include "util/str.h" @@ -13,25 +13,26 @@ namespace basil { using namespace jasmine; enum LocationType { - SSA_NONE, - SSA_LOCAL, - SSA_IMMEDIATE, - SSA_CONSTANT, - SSA_LABEL + LOC_NONE, + LOC_LOCAL, + LOC_IMMEDIATE, + LOC_CONSTANT, + LOC_LABEL, + LOC_REGISTER }; struct LocalInfo { string name; u32 index; const Type* type; - x64::Arg value; + i64 reg; + i64 offset; }; struct ConstantInfo { string name; vector data; const Type* type; - x64::Arg value; }; struct Location { @@ -40,42 +41,91 @@ namespace basil { i64 immediate; u32 constant_index; u32 label_index; + u32 reg; }; LocationType type; const Type* value_type(); }; - Location ssa_none(); - Location ssa_immediate(i64 i); - const Type* ssa_type(const Location& loc); - x64::Arg x64_arg(const Location& loc); + extern const u8 BINARY_INSN; + + enum InsnType { + LOAD_INSN = 0, + STORE_INSN = 1, + LOAD_ARG_INSN = 2, + GOTO_INSN = 3, + IFZERO_INSN = 4, + CALL_INSN = 5, + ADDRESS_INSN = 6, + NOT_INSN = 7, + LOAD_PTR_INSN = 8, + STORE_PTR_INSN = 9, + RET_INSN = 10, + LABEL = 11, + ADD_INSN = 128, + SUB_INSN = 129, + MUL_INSN = 130, + DIV_INSN = 131, + REM_INSN = 132, + AND_INSN = 133, + OR_INSN = 134, + XOR_INSN = 135, + EQ_INSN = 136, + NOT_EQ_INSN = 137, + LESS_INSN = 138, + LESS_EQ_INSN = 139, + GREATER_INSN = 140, + GREATER_EQ_INSN = 141, + }; + + Location loc_none(); + Location loc_immediate(i64 i); + Location loc_label(const string& label); + const Type* type_of(const Location& loc); + + i64 immediate_of(const Location& loc); + const string& label_of(const Location& loc); + LocalInfo& local_of(const Location& loc); + ConstantInfo& constant_of(const Location& loc); class Function; class Insn { protected: - Function* _func; + InsnType _kind; Location _loc; + Function* _func; + vector _succ; + set _in, _out; virtual Location lazy_loc() = 0; friend class Function; void setfunc(Function* func); public: - Insn(); + Insn(InsnType kind); virtual ~Insn(); - Location loc(); + InsnType kind() const; + const Location& loc() const; + Location& loc(); virtual void emit() = 0; virtual void format(stream& io) const = 0; - }; - - u32 ssa_find_label(const string& label); - u32 ssa_add_label(const string& label); - u32 ssa_next_label(); - Location ssa_next_local(const Type* t); - Location ssa_const(u32 label, const string& constant); - void ssa_emit_constants(Object& object); + vector& succ(); + const vector& succ() const; + set& in(); + set& out(); + const set& in() const; + const set& out() const; + virtual void liveout() = 0; + }; + + u32 find_label(const string& label); + u32 add_label(const string& label); + u32 next_label(); + Location next_local(const Type* t); + Location const_loc(u32 label, const string& constant); + void emit_constants(Object& object); class Function { vector _fns; @@ -84,6 +134,10 @@ namespace basil { vector _locals; map _labels; u32 _label; + u32 _end; + Location _ret; + void liveness(); + void to_registers(); Function(u32 label); public: Function(const string& label); @@ -94,12 +148,14 @@ namespace basil { Function& create_function(const string& name); Location create_local(const Type* t); Location create_local(const string& name, const Type* t); - Location next_local(const Location& loc); Location add(Insn* insn); u32 label() const; void allocate(); void emit(Object& obj); void format(stream& io) const; + u32 end_label() const; + const Location& ret_loc() const; + Insn* last() const; }; class LoadInsn : public Insn { @@ -111,18 +167,19 @@ namespace basil { void emit() override; void format(stream& io) const override; + void liveout() override; }; class StoreInsn : public Insn { Location _dest, _src; - bool _init; protected: Location lazy_loc() override; public: - StoreInsn(Location dest, Location src, bool init); + StoreInsn(Location dest, Location src); void emit() override; void format(stream& io) const override; + void liveout() override; }; class LoadPtrInsn : public Insn { @@ -136,6 +193,7 @@ namespace basil { void emit() override; void format(stream& io) const override; + void liveout() override; }; class StorePtrInsn : public Insn { @@ -148,6 +206,7 @@ namespace basil { void emit() override; void format(stream& io) const override; + void liveout() override; }; class AddressInsn : public Insn { @@ -160,6 +219,7 @@ namespace basil { void emit() override; void format(stream& io) const override; + void liveout() override; }; class BinaryInsn : public Insn { @@ -167,16 +227,21 @@ namespace basil { protected: Location _left, _right; public: - BinaryInsn(const char* name, Location left, Location right); + BinaryInsn(InsnType kind, const char* name, Location left, Location right); void format(stream& io) const override; + void liveout() override; + Location& left(); + const Location& left() const; + Location& right(); + const Location& right() const; }; class BinaryMathInsn : public BinaryInsn { protected: Location lazy_loc() override; public: - BinaryMathInsn(const char* name, Location left, + BinaryMathInsn(InsnType kind, const char* name, Location left, Location right); }; @@ -214,7 +279,7 @@ namespace basil { protected: Location lazy_loc() override; public: - BinaryLogicInsn(const char* name, Location left, + BinaryLogicInsn(InsnType kind, const char* name, Location left, Location right); }; @@ -245,13 +310,14 @@ namespace basil { void emit() override; void format(stream& io) const override; + void liveout() override; }; class BinaryEqualityInsn : public BinaryInsn { protected: Location lazy_loc() override; public: - BinaryEqualityInsn(const char* name, Location left, + BinaryEqualityInsn(InsnType kind, const char* name, Location left, Location right); }; @@ -271,7 +337,7 @@ namespace basil { protected: Location lazy_loc() override; public: - BinaryRelationInsn(const char* name, Location left, + BinaryRelationInsn(InsnType kind, const char* name, Location left, Location right); }; @@ -308,6 +374,7 @@ namespace basil { void emit() override; void format(stream& io) const override; + void liveout() override; }; class LoadArgumentInsn : public Insn { @@ -320,31 +387,21 @@ namespace basil { void emit() override; void format(stream& io) const override; - }; - - class StoreArgumentInsn : public Insn { - Location _src; - u32 _index; - const Type* _type; - protected: - Location lazy_loc() override; - public: - StoreArgumentInsn(Location src, u32 index, const Type* type); - - void emit() override; - void format(stream& io) const override; + void liveout() override; }; class CallInsn : public Insn { Location _fn; + vector _args; const Type* _ret; protected: Location lazy_loc() override; public: - CallInsn(Location fn, const Type* ret); + CallInsn(Location fn, const vector& args, const Type* ret); void emit() override; void format(stream& io) const override; + void liveout() override; }; class Label : public Insn { @@ -356,6 +413,7 @@ namespace basil { void emit() override; void format(stream& io) const override; + void liveout() override; }; class GotoInsn : public Insn { @@ -367,6 +425,7 @@ namespace basil { void emit() override; void format(stream& io) const override; + void liveout() override; }; class IfZeroInsn : public Insn { @@ -380,6 +439,7 @@ namespace basil { void emit() override; void format(stream& io) const override; + void liveout() override; }; } diff --git a/lex.cpp b/compiler/lex.cpp similarity index 96% rename from lex.cpp rename to compiler/lex.cpp index 2a844d0..3851d71 100644 --- a/lex.cpp +++ b/compiler/lex.cpp @@ -1,8 +1,8 @@ #include "lex.h" #include "util/io.h" #include "errors.h" -#include -#include +#include "ctype.h" +#include "stdlib.h" namespace basil { Token::Token(TokenType type_in, const const_slice& value_in, u32 line_in, u32 column_in): @@ -15,7 +15,7 @@ namespace basil { static const char* TOKEN_NAMES[NUM_TOKEN_TYPES] = { "none", "int", "symbol", "string", "coeff", "left paren", "right paren", "left bracket", "right bracket", "left brace", "right brace", - "semicolon", "dot", "colon", "pipe", "plus", "minus", "quote", + "semicolon", "dot", "comma", "colon", "pipe", "plus", "minus", "quote", "newline" }; @@ -27,7 +27,7 @@ namespace basil { T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 10 T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 18 T_NONE, T_NONE, T_STRING, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 20 - T_LPAREN, T_RPAREN, T_NONE, T_NONE, T_NONE, T_NONE, T_DOT, T_NONE, // 28 + T_LPAREN, T_RPAREN, T_NONE, T_NONE, T_COMMA, T_NONE, T_DOT, T_NONE, // 28 T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 30 T_NONE, T_NONE, T_COLON, T_SEMI, T_NONE, T_NONE, T_NONE, T_NONE, // 38 T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 40 diff --git a/lex.h b/compiler/lex.h similarity index 95% rename from lex.h rename to compiler/lex.h index ec33b1a..2239714 100644 --- a/lex.h +++ b/compiler/lex.h @@ -9,7 +9,7 @@ namespace basil { T_NONE, T_INT, T_SYMBOL, T_STRING, T_COEFF, T_LPAREN, T_RPAREN, T_LBRACK, T_RBRACK, T_LBRACE, T_RBRACE, - T_SEMI, T_DOT, T_COLON, T_PIPE, + T_SEMI, T_DOT, T_COMMA, T_COLON, T_PIPE, T_PLUS, T_MINUS, T_QUOTE, T_NEWLINE, NUM_TOKEN_TYPES diff --git a/main.cpp b/compiler/main.cpp similarity index 97% rename from main.cpp rename to compiler/main.cpp index e1f1e47..12be4ed 100644 --- a/main.cpp +++ b/compiler/main.cpp @@ -7,18 +7,12 @@ using namespace basil; -#define PRINT_TOKENS false -#define PRINT_AST false -#define PRINT_EVAL true -#define PRINT_SSA false -#define PRINT_ASM false - void print_banner() { println(""); println("┌────────────────────────────────────────┐"); println("│ │"); println("│ ", BOLDGREEN, R"(𝐵𝑎𝑠𝑖𝑙)", - RESET, " — version 0.1 │"); + RESET, " — version 0.1 │"); println("│ │"); println("└────────────────────────────────────────┘"); println(RESET); @@ -61,13 +55,13 @@ Value print_ast(ref env, const Value& args) { return Value(VOID); } -Value print_ssa(ref env, const Value& args) { +Value print_ir(ref env, const Value& args) { if (!args.get_product()[0].is_bool()) { err(args.get_product()[0].loc(), "Print SSA command requires bool, '", args.get_product()[0], "' provided."); return ERROR; } - basil::print_ssa(args.get_product()[0].get_bool()); + basil::print_ir(args.get_product()[0].get_bool()); return Value(VOID); } @@ -89,7 +83,7 @@ Value repl_help(ref env, const Value& args) { println(" - $print-tokens => toggles token printing."); println(" - $print-parse => toggles parse tree printing."); println(" - $print-ast => toggles AST printing."); - println(" - $print-ssa => toggles SSA printing."); + println(" - $print-ir => toggles IR printing."); println(" - $print-asm => toggles assembly printing."); println(""); return Value(VOID); @@ -111,10 +105,9 @@ int main(int argc, char** argv) { root->def("$print-tokens", new FunctionValue(root, print_tokens, 1), 1); root->def("$print-parse", new FunctionValue(root, print_parse, 1), 1); root->def("$print-ast", new FunctionValue(root, print_ast, 1), 1); - root->def("$print-ssa", new FunctionValue(root, print_ssa, 1), 1); + root->def("$print-ir", new FunctionValue(root, print_ir, 1), 1); root->def("$print-asm", new FunctionValue(root, print_asm, 1), 1); - Env global_env(root); - ref global(global_env); + ref global = newref(root); Function main_fn("main"); while (!repl_done) repl(global, src, main_fn); @@ -127,6 +120,10 @@ int main(int argc, char** argv) { Source src(argv[2]); return run(src); } + else if (argc == 3 && string(argv[1]) == "build") { + Source src(argv[2]); + return build(src, argv[2]); + } else if (argc > 2 && string(argv[1]) == "exec") { Source src; string code; @@ -152,6 +149,7 @@ int main(int argc, char** argv) { println(" - basil help => prints usage information."); println(" - basil intro => runs interactive introduction."); println(" - basil exec => executes ."); + println(" - basil build => compiles to object."); println(""); } @@ -811,8 +809,8 @@ R"(Enter ':okay!' into the prompt when you're ready to continue. )", Value("okay!")}); } -#include -#include +#include "stdlib.h" +#include "time.h" void print_intro_conclusion() { println(BOLDGREEN, "Congratulations! \n", RESET); @@ -865,8 +863,7 @@ int intro() { }; while (!repl_done) { - Env global_env(root); - ref global(global_env); + ref global = newref(root); Function main_fn("main"); bool is_running_intro = running_intro; @@ -909,4 +906,4 @@ int intro() { } return 0; -} \ No newline at end of file +} diff --git a/native.cpp b/compiler/native.cpp similarity index 52% rename from native.cpp rename to compiler/native.cpp index a039624..6cc300e 100644 --- a/native.cpp +++ b/compiler/native.cpp @@ -1,7 +1,7 @@ #include "native.h" #include "values.h" #include "util/io.h" -#include +#include "stdlib.h" namespace basil { using namespace jasmine; @@ -23,14 +23,14 @@ namespace basil { return result; } - i64 _listlen(void* list) { - u32 size = 0; - while (list) { - list = *((void**)list + 1); - size ++; - } - return size; - } + i64 _listlen(void* list) { + u32 size = 0; + while (list) { + list = *((void**)list + 1); + size ++; + } + return size; + } void _display_int(i64 value) { println(value); @@ -61,6 +61,30 @@ namespace basil { println(")"); } + void _display_int_list(void* value) { + print("("); + bool first = true; + while (value) { + i64 i = *(i64*)value; + print(first ? "" : " ", i); + value = *((void**)value + 1); + first = false; + } + println(")"); + } + + void _display_bool_list(void* value) { + print("("); + bool first = true; + while (value) { + u64 i = *(u64*)value; + print(first ? "" : " ", i != 0); + value = *((void**)value + 1); + first = false; + } + println(")"); + } + void _display_symbol_list(void* value) { print("("); bool first = true; @@ -95,25 +119,25 @@ namespace basil { else if (elt == STRING) _display_native_string_list(list); } - i64 _strcmp(const char *a, const char *b) { - while (*a && *b && *a == *b) a ++, b ++; - return *(const unsigned char*)a - *(const unsigned char*)b; - } + i64 _strcmp(const char *a, const char *b) { + while (*a && *b && *a == *b) a ++, b ++; + return *(const unsigned char*)a - *(const unsigned char*)b; + } - i64 _strlen(const char *s) { - const char *copy = s; - while (*s++); - return s - copy - 1; - } + i64 _strlen(const char *s) { + const char *copy = s; + while (*s++); + return s - copy - 1; + } - const u8* _read_line() { - string s; - while (_stdin.peek() != '\n') { s += _stdin.read(); } - u8* buf = new u8[s.size() + 1]; - for (u32 i = 0; i < s.size(); i ++) buf[i] = s[i]; - buf[s.size()] = '\0'; - return buf; - } + const u8* _read_line() { + string s; + while (_stdin.peek() != '\n') { s += _stdin.read(); } + u8* buf = new u8[s.size() + 1]; + for (u32 i = 0; i < s.size(); i ++) buf[i] = s[i]; + buf[s.size()] = '\0'; + return buf; + } i64 _read_int() { i64 i; @@ -121,39 +145,57 @@ namespace basil { return i; } - const u8* _read_word() { - string s; - read(_stdin, s); - u8* buf = new u8[s.size() + 1]; - for (u32 i = 0; i < s.size(); i ++) buf[i] = s[i]; - buf[s.size()] = '\0'; - return buf; - } + const u8* _read_word() { + string s; + read(_stdin, s); + u8* buf = new u8[s.size() + 1]; + for (u32 i = 0; i < s.size(); i ++) buf[i] = s[i]; + buf[s.size()] = '\0'; + return buf; + } - u8 _char_at(const char *s, i64 idx) { - return s[idx]; - } + u8 _char_at(const char *s, i64 idx) { + return s[idx]; + } - // const u8* _substr(const char *s, i64 start, i64 end) + const u8* _strcat(const char* a, const char* b) { + u8* buf = new u8[_strlen(a) + _strlen(b) + 1]; + u32 i = 0; + for (;*a;a ++) buf[i ++] = *a; + for (;*b;b ++) buf[i ++] = *b; + buf[i] = '\0'; + return buf; + } + + const u8* _substr(const char *s, i64 start, i64 end) { + if (end < start) return new u8[1]{'\0'}; + u8* buf = new u8[end - start + 1]; + i64 i = start; + for (; i < end; i ++) buf[i - start] = s[i]; + buf[i + 1] = '\0'; + return buf; + } void add_native_functions(Object& object) { add_native_function(object, "_cons", (void*)_cons); - add_native_function(object, "_strcmp", (void*)_strcmp); - add_native_function(object, "_strlen", (void*)_strlen); - add_native_function(object, "_read_line", (void*)_read_line); - add_native_function(object, "_read_int", (void*)_read_int); - add_native_function(object, "_read_word", (void*)_read_word); - add_native_function(object, "_char_at", (void*)_char_at); - add_native_function(object, "_listlen", (void*)_listlen); + add_native_function(object, "_strcmp", (void*)_strcmp); + add_native_function(object, "_strlen", (void*)_strlen); + add_native_function(object, "_strcat", (void*)_strcat); + add_native_function(object, "_substr", (void*)_substr); + add_native_function(object, "_read_line", (void*)_read_line); + add_native_function(object, "_read_int", (void*)_read_int); + add_native_function(object, "_read_word", (void*)_read_word); + add_native_function(object, "_char_at", (void*)_char_at); + add_native_function(object, "_listlen", (void*)_listlen); add_native_function(object, "_display_int", (void*)_display_int); add_native_function(object, "_display_symbol", (void*)_display_symbol); add_native_function(object, "_display_bool", (void*)_display_bool); add_native_function(object, "_display_string", (void*)_display_string); - add_native_function(object, "_display_int_list", (void*)_display_list); + add_native_function(object, "_display_int_list", (void*)_display_int_list); add_native_function(object, "_display_symbol_list", (void*)_display_symbol_list); - add_native_function(object, "_display_bool_list", (void*)_display_list); - add_native_function(object, "_display_string_list", (void*)_display_list); + add_native_function(object, "_display_bool_list", (void*)_display_bool_list); + add_native_function(object, "_display_string_list", (void*)_display_native_string_list); } } \ No newline at end of file diff --git a/native.h b/compiler/native.h similarity index 93% rename from native.h rename to compiler/native.h index c576cbe..af6c5ff 100644 --- a/native.h +++ b/compiler/native.h @@ -2,7 +2,7 @@ #define BASIL_NATIVE_H #include "util/defs.h" -#include "ssa.h" +#include "ir.h" #include "jasmine/x64.h" namespace basil { diff --git a/compiler/ops.cpp b/compiler/ops.cpp new file mode 100644 index 0000000..67fbb53 --- /dev/null +++ b/compiler/ops.cpp @@ -0,0 +1,545 @@ +#include "ops.h" + +#include "jasmine/x64.h" + +namespace basil { + static Architecture _arch = DEFAULT_ARCH; + + Architecture arch() { + return _arch; + } + + void set_arch(Architecture arch) { + _arch = arch; + } + + u32 arg_register(u32 i) { + switch (_arch) { + case X86_64: { + using namespace x64; + static u32 ARG_REGISTERS[] = { RDI, RSI, RCX, RDX, R8, R9 }; + return ARG_REGISTERS[i]; + } + default: + return 0; + } + } + + u32 clobber_register(u32 i) { + switch (_arch) { + case X86_64: { + using namespace x64; + static u32 CLOBBER_REGISTERS[] = { RAX, RDX, RCX, RBX }; + return CLOBBER_REGISTERS[i]; + } + default: + return 0; + } + } + + vector allocatable_registers() { + switch (_arch) { + case X86_64: { + using namespace x64; + static Register ALLOCATABLE_REGISTERS[] = { + RBX, R8, R9, R10, R11, R12, R13, R14, R15 + }; + vector regs; + for (i64 i = sizeof(ALLOCATABLE_REGISTERS) / sizeof(Register) - 1; i >= 0; i --) + regs.push(u32(ALLOCATABLE_REGISTERS[i])); + return regs; + } + default: + return {}; + } + } + + x64::Arg x64_clobber(u32 i) { + return x64::r64((x64::Register)clobber_register(i)); + } + + x64::Arg x64_param(u32 i) { + return x64::r64((x64::Register)arg_register(i)); + } + + x64::Arg x64_arg(const Location& src) { + using namespace x64; + switch (src.type) { + case LOC_NONE: + return imm64(0); + case LOC_REGISTER: + return r64((Register)src.reg); + case LOC_LABEL: + return label64(global((const char*)label_of(src).raw())); + case LOC_LOCAL: + if (local_of(src).reg >= 0) return r64((Register)local_of(src).reg); + else return m64(RBP, local_of(src).offset); + case LOC_CONSTANT: + return label64(global((const char*)constant_of(src).name.raw())); + case LOC_IMMEDIATE: + return imm64(src.immediate); + } + } + + const x64::Arg& x64_to_register(const x64::Arg& src, const x64::Arg& clobber) { + using namespace x64; + if (is_memory(src.type)) { + mov(clobber, src); + return clobber; + } + else return src; + } + + void x64_move(const x64::Arg& dest, const x64::Arg& src, const x64::Arg& clobber) { + using namespace x64; + if (dest == src) return; + if (is_memory(dest.type) && is_memory(src.type)) { + mov(clobber, src); + mov(dest, clobber); + } + else mov(dest, src); + } + + void x64_binary(const x64::Arg& dest, const x64::Arg& lhs, + const x64::Arg& rhs, const x64::Arg& clobber, void(*op)(const x64::Arg&, const x64::Arg&, x64::Size)) { + using namespace x64; + if (is_memory(dest.type) && (is_memory(lhs.type) || is_memory(rhs.type))) { + x64_move(clobber, lhs, clobber); + op(clobber, rhs, AUTO); + x64_move(dest, clobber, clobber); + } + else { + x64_move(dest, lhs, clobber); + op(dest, rhs, AUTO); + } + } + + void x64_compare(const x64::Arg& lhs, const x64::Arg& rhs, const x64::Arg& clobber) { + using namespace x64; + if (is_memory(lhs.type) && is_memory(rhs.type)) { + x64_move(clobber, lhs, clobber); + cmp(clobber, rhs); + } + else cmp(lhs, rhs, AUTO); + } + + void store(const Location& dest, const Location& src, u32 offset) { + switch (_arch) { + case X86_64: { + using namespace x64; + auto d = x64_to_register(x64_arg(dest), x64_clobber(1)), + s = x64_to_register(x64_arg(src), x64_clobber(0)); + auto m = m64(d.data.reg, offset); + x64_move(m, s, x64_clobber(0)); + return; + } + default: + return; + } + } + + void load(const Location& dest, const Location& src, u32 offset) { + switch (_arch) { + case X86_64: { + using namespace x64; + auto d = x64_arg(dest), + s = x64_to_register(x64_arg(src), x64_clobber(1)); + auto m = m64(s.data.reg, offset); + x64_move(d, m, x64_clobber(0)); + return; + } + default: + return; + } + } + + void move(const Location& dest, const Location& src) { + switch (_arch) { + case X86_64: { + using namespace x64; + auto d = x64_arg(dest), s = x64_arg(src); + x64_move(d, s, x64_clobber(0)); + return; + } + default: + return; + } + } + + void add(const Location& dest, const Location& lhs, const Location& rhs) { + switch (_arch) { + case X86_64: { + using namespace x64; + auto d = x64_arg(dest), l = x64_arg(lhs), r = x64_arg(rhs); + if (is_register(d.type) && !is_memory(l.type) && !is_memory(r.type)) { + if (d.data.reg == l.data.reg) // no need for two steps + if (is_immediate(r.type) && r.data.imm64 == 1) + return inc(d); + else if (is_immediate(r.type) && r.data.imm64 == -1) + return dec(d); + else return add(d, r); + else if (d.data.reg == r.data.reg) // no need for two steps + if (is_immediate(l.type) && l.data.imm64 == 1) + return inc(d); + else if (is_immediate(l.type) && l.data.imm64 == -1) + return dec(d); + else return add(d, l); + else if (is_register(l.type) && is_immediate(r.type)) + return lea(d, m64(l.data.reg, r.data.imm64)); + else if (is_immediate(l.type) && is_register(r.type)) + return lea(d, m64(r.data.reg, l.data.imm64)); + else if (is_register(l.type) && is_register(r.type)) + return lea(d, m64(l.data.reg, r.data.reg, SCALE1, 0)); + } + x64_binary(x64_arg(dest), x64_arg(lhs), x64_arg(rhs), x64_clobber(0), x64::add); + return; + } + default: + return; + } + } + + void sub(const Location& dest, const Location& lhs, const Location& rhs) { + switch (_arch) { + case X86_64: { + using namespace x64; + auto d = x64_arg(dest), l = x64_arg(lhs), r = x64_arg(rhs); + if (is_register(d.type) && !is_memory(l.type) && !is_memory(r.type)) { + if (d.data.reg == l.data.reg) // no need for two steps + if (is_immediate(r.type) && r.data.imm64 == 1) + return dec(d); + else if (is_immediate(r.type) && r.data.imm64 == -1) + return inc(d); + else return sub(d, r); + else if (is_register(l.type) && is_immediate(r.type)) + return lea(d, m64(l.data.reg, -r.data.imm64)); + } + x64_binary(x64_arg(dest), x64_arg(lhs), x64_arg(rhs), x64_clobber(0), x64::sub); + return; + } + default: + return; + } + } + + void mul(const Location& dest, const Location& lhs, const Location& rhs) { + switch (_arch) { + case X86_64: { + using namespace x64; + auto d = x64_arg(dest); + auto t = is_memory(d.type) ? x64_clobber(0) : d; + x64_move(t, x64_arg(lhs), x64_clobber(0)); + auto r = x64_arg(rhs); + if (is_immediate(r.type)) { + x64_move(x64_clobber(1), r, x64_clobber(1)); + x64::imul(t, x64_clobber(1)); + } + else x64::imul(t, r); + if (is_memory(d.type)) mov(d, t); + return; + } + default: + return; + } + } + + void div(const Location& dest, const Location& lhs, const Location& rhs) { + switch (_arch) { + case X86_64: { + using namespace x64; + x64_move(x64_clobber(0), x64_arg(lhs), x64_clobber(0)); + cdq(); + auto r = x64_arg(rhs); + if (is_immediate(r.type)) { + x64_move(x64_clobber(2), r, x64_clobber(2)); + idiv(x64_clobber(2)); + } + else idiv(r); + x64_move(x64_arg(dest), x64_clobber(0), x64_clobber(0)); // dest <- rax + return; + } + default: + return; + } + } + + void rem(const Location& dest, const Location& lhs, const Location& rhs) { + switch (_arch) { + case X86_64: { + using namespace x64; + x64_move(x64_clobber(0), x64_arg(lhs), x64_clobber(0)); + cdq(); + auto r = x64_arg(rhs); + if (is_immediate(r.type)) { + x64_move(x64_clobber(2), r, x64_clobber(2)); + idiv(x64_clobber(2)); + } + else idiv(r); + x64_move(x64_arg(dest), x64_clobber(1), x64_clobber(0)); // dest <- rdx + return; + } + default: + return; + } + } + + void neg(const Location& dest, const Location& src) { + // + } + + void and_op(const Location& dest, const Location& lhs, const Location& rhs) { + switch (_arch) { + case X86_64: + x64_binary(x64_arg(dest), x64_arg(lhs), x64_arg(rhs), x64_clobber(0), x64::and_); + return; + default: + return; + } + } + + void or_op(const Location& dest, const Location& lhs, const Location& rhs) { + switch (_arch) { + case X86_64: + x64_binary(x64_arg(dest), x64_arg(lhs), x64_arg(rhs), x64_clobber(0), x64::or_); + return; + default: + return; + } + } + + void xor_op(const Location& dest, const Location& lhs, const Location& rhs) { + switch (_arch) { + case X86_64: + x64_binary(x64_arg(dest), x64_arg(lhs), x64_arg(rhs), x64_clobber(0), x64::xor_); + return; + default: + return; + } + } + + void not_op(const Location& dest, const Location& src) { + switch (_arch) { + case X86_64: { + using namespace x64; + x64_binary(x64_arg(dest), x64_arg(src), imm64(0), x64_clobber(0), x64::cmp); + setcc(x64_arg(dest), EQUAL); + return; + } + default: + return; + } + } + + void equal(const Location& dest, const Location& lhs, const Location& rhs) { + switch (_arch) { + case X86_64: + x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); + x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); + x64::setcc(x64_arg(dest), x64::EQUAL); + return; + default: + return; + } + } + + void not_equal(const Location& dest, const Location& lhs, const Location& rhs) { + switch (_arch) { + case X86_64: + x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); + x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); + x64::setcc(x64_arg(dest), x64::NOT_EQUAL); + return; + default: + return; + } + } + + void less(const Location& dest, const Location& lhs, const Location& rhs) { + switch (_arch) { + case X86_64: + x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); + x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); + x64::setcc(x64_arg(dest), x64::LESS); + return; + default: + return; + } + } + + void less_equal(const Location& dest, const Location& lhs, const Location& rhs) { + switch (_arch) { + case X86_64: + x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); + x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); + x64::setcc(x64_arg(dest), x64::LESS_OR_EQUAL); + return; + default: + return; + } + } + + void greater(const Location& dest, const Location& lhs, const Location& rhs) { + switch (_arch) { + case X86_64: + x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); + x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); + x64::setcc(x64_arg(dest), x64::GREATER); + return; + default: + return; + } + } + + void greater_equal(const Location& dest, const Location& lhs, const Location& rhs) { + switch (_arch) { + case X86_64: + x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); + x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); + x64::setcc(x64_arg(dest), x64::GREATER_OR_EQUAL); + return; + default: + return; + } + } + + void lea(const Location& dest, const Location& src) { + switch (_arch) { + case X86_64: { + using namespace x64; + auto d = x64_arg(dest); + if (is_memory(d.type)) { + lea(x64_clobber(0), x64_arg(src)); + x64_move(d, x64_clobber(0), x64_clobber(0)); + } + else lea(d, x64_arg(src)); + return; + } + default: + return; + } + } + + void jump(const Location& dest) { + switch (_arch) { + case X86_64: + x64::jmp(x64_arg(dest)); + return; + default: + return; + } + } + + void jump_if_zero(const Location& dest, const Location& cond) { + switch (_arch) { + case X86_64: + x64_compare(x64_arg(cond), x64::imm64(0), x64_clobber(0)); + x64::jcc(x64_arg(dest), x64::EQUAL); + return; + default: + return; + } + } + + void set_arg(u32 i, const Location& src) { + switch (_arch) { + case X86_64: + x64_move(x64_param(i), x64_arg(src), x64_clobber(0)); + return; + default: + return; + } + } + + void get_arg(const Location& dest, u32 i) { + switch (_arch) { + case X86_64: + x64_move(x64_arg(dest), x64_param(i), x64_clobber(0)); + return; + default: + return; + } + } + + void call(const Location& dest, const Location& func) { + switch (_arch) { + case X86_64: + x64::call(x64_arg(func)); + x64_move(x64_arg(dest), x64_clobber(0), x64_clobber(0)); + return; + default: + return; + } + } + + void global_label(const string& name) { + switch (_arch) { + case X86_64: + x64::label(jasmine::global((const char*)name.raw())); + return; + default: + return; + } + } + + void local_label(const string& name) { + switch (_arch) { + case X86_64: + x64::label(jasmine::local((const char*)name.raw())); + return; + default: + return; + } + } + + void push(const Location& src) { + switch (_arch) { + case X86_64: + x64::push(x64_arg(src)); + return; + default: + return; + } + } + + void pop(const Location& dest) { + switch (_arch) { + case X86_64: + x64::pop(x64_arg(dest)); + return; + default: + return; + } + } + + void open_frame(u32 size) { + switch (_arch) { + case X86_64: { + using namespace x64; + if (size) { + push(r64(RBP)); + mov(r64(RBP), r64(RSP)); + sub(r64(RSP), imm64(size)); + } + return; + } + default: + return; + } + } + + void close_frame(u32 size) { + switch (_arch) { + case X86_64: { + using namespace x64; + if (size) { + mov(r64(RSP), r64(RBP)); + pop(r64(RBP)); + } + ret(); + return; + } + default: + return; + } + } +} \ No newline at end of file diff --git a/compiler/ops.h b/compiler/ops.h new file mode 100644 index 0000000..ed47900 --- /dev/null +++ b/compiler/ops.h @@ -0,0 +1,50 @@ +#ifndef BASIL_OPS_H +#define BASIL_OPS_H + +#include "util/defs.h" +#include "util/vec.h" +#include "ir.h" +#include "jasmine/target.h" + +namespace basil { + Architecture arch(); + void set_arch(Architecture arch); + + u32 arg_register(u32 i); + u32 clobber_register(u32 i); + vector allocatable_registers(); + + void store(const Location& dest, const Location& src, u32 offset); + void load(const Location& dest, const Location& src, u32 offset); + void lea(const Location& dest, const Location& src); + void move(const Location& dest, const Location& src); + void add(const Location& dest, const Location& lhs, const Location& rhs); + void sub(const Location& dest, const Location& lhs, const Location& rhs); + void mul(const Location& dest, const Location& lhs, const Location& rhs); + void div(const Location& dest, const Location& lhs, const Location& rhs); + void rem(const Location& dest, const Location& lhs, const Location& rhs); + void neg(const Location& dest, const Location& src); + void and_op(const Location& dest, const Location& lhs, const Location& rhs); + void or_op(const Location& dest, const Location& lhs, const Location& rhs); + void xor_op(const Location& dest, const Location& lhs, const Location& rhs); + void not_op(const Location& dest, const Location& src); + void equal(const Location& dest, const Location& lhs, const Location& rhs); + void not_equal(const Location& dest, const Location& lhs, const Location& rhs); + void less(const Location& dest, const Location& lhs, const Location& rhs); + void less_equal(const Location& dest, const Location& lhs, const Location& rhs); + void greater(const Location& dest, const Location& lhs, const Location& rhs); + void greater_equal(const Location& dest, const Location& lhs, const Location& rhs); + void jump(const Location& dest); + void jump_if_zero(const Location& dest, const Location& cond); + void set_arg(u32 i, const Location& src); + void get_arg(const Location& dest, u32 i); + void call(const Location& dest, const Location& func); + void global_label(const string& name); + void local_label(const string& name); + void push(const Location& src); + void pop(const Location& dest); + void open_frame(u32 size); + void close_frame(u32 size); +} + +#endif \ No newline at end of file diff --git a/parse.cpp b/compiler/parse.cpp similarity index 89% rename from parse.cpp rename to compiler/parse.cpp index b89ba47..ebe019a 100644 --- a/parse.cpp +++ b/compiler/parse.cpp @@ -57,12 +57,31 @@ namespace basil { void parse_enclosed(TokenView& view, vector& terms, TokenType terminator, u32 indent) { + vector tuple_elements; + bool errored = false; while (view.peek().type != terminator) { while (view.peek().type == T_NEWLINE) view.read(); if (!view.peek() && out_of_input(view)) return; terms.push(parse(view, indent)); + if (view.peek().type == T_COMMA) { + if (terminator != T_RPAREN && !errored) { // not enclosed by parens + err(view.peek(), "Tuple literals require parentheses."), errored = true; + tuple_elements.push(error()); + } + else if (!errored) { // correct usage + tuple_elements.push(list_of(terms)); + terms.clear(); + } + view.read(); + } } view.read(); + if (tuple_elements.size() > 0) { + tuple_elements.push(list_of(terms)); + terms.clear(); + terms.push(Value("tuple-of")); + for (const Value& v : tuple_elements) terms.push(v); + } } void parse_block(TokenView& view, vector& terms, @@ -169,6 +188,7 @@ namespace basil { view.read(); vector terms; parse_enclosed(view, terms, T_RPAREN, indent); + for (const Value& v : terms) if (v.is_error()) return error(); Value v = list_of(terms); v.set_location(first); return v; @@ -178,6 +198,7 @@ namespace basil { vector terms; terms.push(Value("list-of")); parse_enclosed(view, terms, T_RBRACK, indent); + for (const Value& v : terms) if (v.is_error()) return error(); Value v = list_of(terms); v.set_location(first); return v; @@ -187,6 +208,7 @@ namespace basil { vector terms; terms.push(Value("set-of")); parse_enclosed(view, terms, T_RBRACE, indent); + for (const Value& v : terms) if (v.is_error()) return error(); Value v = list_of(terms); v.set_location(first); return v; @@ -195,6 +217,7 @@ namespace basil { view.read(); vector terms; parse_enclosed(view, terms, T_PIPE, indent); + for (const Value& v : terms) if (v.is_error()) return error(); Value v = cons(Value("splice"), list_of(terms)); v.set_location(first); return v; diff --git a/parse.h b/compiler/parse.h similarity index 100% rename from parse.h rename to compiler/parse.h diff --git a/source.cpp b/compiler/source.cpp similarity index 100% rename from source.cpp rename to compiler/source.cpp diff --git a/source.h b/compiler/source.h similarity index 100% rename from source.h rename to compiler/source.h diff --git a/compiler/ssa.cpp b/compiler/ssa.cpp new file mode 100644 index 0000000..ec547e0 --- /dev/null +++ b/compiler/ssa.cpp @@ -0,0 +1,507 @@ +#include "ssa.h" +#include "env.h" +#include "values.h" + +namespace basil { + static map id_name_table; + static vector id_names; + static vector id_counts; + + SSAIdent next_ident(const string& name = "") { + auto it = id_name_table.find(name); + if (it == id_name_table.end()) { + id_name_table[name] = id_names.size(); + id_names.push(name); + id_counts.push(0); + // start new ident chain for name + return { id_names.size() - 1, id_counts.back() }; + } + else { + // find next ident in chain, incrementing count + return { it->second, ++ id_counts[it->second] }; + } + } + + const u8 MAJOR_MASK = 0b11100000; + const u8 MINOR_MASK = 0b00011000; + + SSANode::SSANode(ref parent, const Type* type, SSAKind kind, const string& name): + _parent(parent), _type(type), _kind(kind), _id(next_ident(name)) {} + + bool SSANode::is(SSAKind kind) const { + return _kind == kind; + } + + ref SSANode::parent() const { + return _parent; + } + + ref& SSANode::parent() { + return _parent; + } + + SSAIdent SSANode::id() const { + return _id; + } + + SSAKind SSANode::kind() const { + return _kind; + } + + const Type* SSANode::type() const { + return _type; + } + + static u64 bb_count = 0; + string next_bb() { + return format(".BB", bb_count ++); + } + + BasicBlock::BasicBlock(const string& label): _label(label) { + if (!_label.size()) _label = next_bb(); + } + + const string& BasicBlock::label() const { + return _label; + } + + u32 BasicBlock::size() const { + return _members.size(); + } + + const ref* BasicBlock::begin() const { + return _members.begin(); + } + + ref* BasicBlock::begin() { + return _members.begin(); + } + + const ref* BasicBlock::end() const { + return _members.end(); + } + + ref* BasicBlock::end() { + return _members.end(); + } + + void BasicBlock::push(ref node) { + _members.push(node); + } + + const vector>& BasicBlock::members() const { + return _members; + } + + vector>& BasicBlock::members() { + return _members; + } + + Location BasicBlock::emit(Function& fn) { + Location loc; + for (auto& member : _members) loc = member->emit(fn); + return loc; + } + + void BasicBlock::format(stream& io) const { + writeln(io, label(), ":"); + for (auto& member : _members) + writeln(io, " ", member); + } + + SSAInt::SSAInt(ref parent, i64 value): + SSANode(parent, INT, SSA_INT), _value(value) {} + + i64 SSAInt::value() const { + return _value; + } + + i64& SSAInt::value() { + return _value; + } + + Location SSAInt::emit(Function& fn) { + return loc_immediate(_value); + } + + void SSAInt::format(stream& io) const { + write(io, id(), " = ", _value); + } + + SSABool::SSABool(ref parent, bool value): + SSANode(parent, BOOL, SSA_BOOL), _value(value) {} + + bool SSABool::value() const { + return _value; + } + + bool& SSABool::value() { + return _value; + } + + Location SSABool::emit(Function& fn) { + return loc_immediate(_value ? 1 : 0); + } + + void SSABool::format(stream& io) const { + write(io, id(), " = ", _value); + } + + SSAString::SSAString(ref parent, const string& value): + SSANode(parent, STRING, SSA_STRING), _value(value) {} + + const string& SSAString::value() const { + return _value; + } + + string& SSAString::value() { + return _value; + } + + Location SSAString::emit(Function& fn) { + return loc_none(); // todo + } + + void SSAString::format(stream& io) const { + write(io, id(), " = \"", _value, "\""); + } + + SSAVoid::SSAVoid(ref parent): SSANode(parent, VOID, SSA_VOID) {} + + Location SSAVoid::emit(Function& fn) { + return loc_immediate(0); + } + + void SSAVoid::format(stream& io) const { + write(io, id(), " = []"); + } + + SSASymbol::SSASymbol(ref parent, u64 value): + SSANode(parent, SYMBOL, SSA_SYMBOL), _value(value) {} + + u64 SSASymbol::value() const { + return _value; + } + + u64& SSASymbol::value() { + return _value; + } + + Location SSASymbol::emit(Function& fn) { + return loc_immediate(_value); + } + + void SSASymbol::format(stream& io) const { + write(io, id(), " = ", symbol_for(_value)); + } + + SSAFunction::SSAFunction(ref parent, const Type* type, u64 name): + SSANode(parent, type, SSA_FUNCTION, symbol_for(name)), _name(name) {} + + Location SSAFunction::emit(Function& fn) { + return loc_label(symbol_for(_name)); + } + + void SSAFunction::format(stream& io) const { + write(io, id(), " = &", symbol_for(_name)); + } + + SSALoadLabel::SSALoadLabel(ref parent, const Type* type, u64 name): + SSANode(parent, type, SSA_LOAD_LABEL, symbol_for(name)), _name(name) {} + + Location SSALoadLabel::emit(Function& fn) { + return loc_label(symbol_for(_name)); + } + + void SSALoadLabel::format(stream& io) const { + write(io, id(), " = &", symbol_for(_name)); + } + + SSAStore::SSAStore(ref parent, ref env, u64 name, ref value): + SSANode(parent, value->type(), SSA_STORE, symbol_for(name)), _env(env), _value(value) {} + + Location SSAStore::emit(Function& fn) { + const string& name = id_names[id().base]; + auto def = _env->find(name); + if (!def) return loc_none(); + if (def->location.type == LOC_NONE) return fn.add(new StoreInsn(def->location = + fn.create_local(name, basil::INT), _value->emit(fn))); + return fn.add(new StoreInsn(def->location, _value->emit(fn))); + } + + void SSAStore::format(stream& io) const { + write(io, id(), " = ", _value->id()); + } + + SSABinary::SSABinary(ref parent, const Type* type, SSAKind kind, ref left, ref right): + SSANode(parent, type, kind), _left(left), _right(right) {} + + ref SSABinary::left() const { + return _left; + } + + ref& SSABinary::left() { + return _left; + } + + ref SSABinary::right() const { + return _right; + } + + ref& SSABinary::right() { + return _right; + } + + Location SSABinary::emit(Function& fn) { + switch (kind()) { + case SSA_ADD: + return fn.add(new AddInsn(_left->emit(fn), _right->emit(fn))); + case SSA_SUB: + return fn.add(new SubInsn(_left->emit(fn), _right->emit(fn))); + case SSA_MUL: + return fn.add(new MulInsn(_left->emit(fn), _right->emit(fn))); + case SSA_DIV: + return fn.add(new DivInsn(_left->emit(fn), _right->emit(fn))); + case SSA_REM: + return fn.add(new RemInsn(_left->emit(fn), _right->emit(fn))); + case SSA_AND: + return fn.add(new AndInsn(_left->emit(fn), _right->emit(fn))); + case SSA_OR: + return fn.add(new OrInsn(_left->emit(fn), _right->emit(fn))); + case SSA_XOR: + return fn.add(new XorInsn(_left->emit(fn), _right->emit(fn))); + case SSA_EQ: + return fn.add(new EqualInsn(_left->emit(fn), _right->emit(fn))); + case SSA_NOT_EQ: + return fn.add(new InequalInsn(_left->emit(fn), _right->emit(fn))); + case SSA_GREATER: + return fn.add(new GreaterInsn(_left->emit(fn), _right->emit(fn))); + case SSA_GREATER_EQ: + return fn.add(new GreaterEqualInsn(_left->emit(fn), _right->emit(fn))); + case SSA_LESS: + return fn.add(new LessInsn(_left->emit(fn), _right->emit(fn))); + case SSA_LESS_EQ: + return fn.add(new LessEqualInsn(_left->emit(fn), _right->emit(fn))); + default: + return loc_none(); + } + } + + void SSABinary::format(stream& io) const { + const char* op; + switch(kind()) { + case SSA_ADD: op = "+"; break; + case SSA_SUB: op = "-"; break; + case SSA_MUL: op = "*"; break; + case SSA_DIV: op = "/"; break; + case SSA_REM: op = "%"; break; + case SSA_AND: op = "and"; break; + case SSA_OR: op = "or"; break; + case SSA_XOR: op = "xor"; break; + case SSA_EQ: op = "=="; break; + case SSA_NOT_EQ: op = "!="; break; + case SSA_LESS: op = "<"; break; + case SSA_LESS_EQ: op = "<="; break; + case SSA_GREATER: op = ">"; break; + case SSA_GREATER_EQ: op = ">="; break; + default: op = "?"; break; + } + write(io, id(), " = ", _left->id(), " ", op, " ", _right->id()); + } + + SSAUnary::SSAUnary(ref parent, const Type* type, SSAKind kind, ref child): + SSANode(parent, type, kind), _child(child) {} + + ref SSAUnary::child() const { + return _child; + } + + ref& SSAUnary::child() { + return _child; + } + + Location SSAUnary::emit(Function& fn) { + switch (kind()) { + case SSA_NOT: + return fn.add(new NotInsn(_child->emit(fn))); + default: + return loc_none(); + } + } + + void SSAUnary::format(stream& io) const { + const char* op; + switch(kind()) { + case SSA_NOT: op = "not"; break; + default: op = "?"; break; + } + write(io, id(), " = ", op, " ", _child->id()); + } + + SSACall::SSACall(ref parent, const Type* type, ref fn, const vector>& args): + SSANode(parent, type, SSA_CALL), _fn(fn), _args(args) {} + + ref SSACall::fn() const { + return _fn; + } + + ref& SSACall::fn() { + return _fn; + } + + const vector>& SSACall::args() const { + return _args; + } + + vector>& SSACall::args() { + return _args; + } + + Location SSACall::emit(Function& fn) { + Location func = _fn->emit(fn); + vector arglocs; + for (auto node : _args) arglocs.push(node->emit(fn)); + for (u32 i = 0; i < _args.size(); i ++) { + if (arglocs[i].type == LOC_LABEL) { + arglocs[i] = fn.add(new AddressInsn(arglocs[i], _args[i]->type())); + } + } + return fn.add(new CallInsn(func, arglocs, type())); + } + + void SSACall::format(stream& io) const { + write(io, id(), " = ", _fn->id(), "("); + bool first = true; + for (auto node : _args) { + write(io, first ? ", " : "", node->id()); + first = false; + } + write(io, ")"); + } + + SSARet::SSARet(ref parent, ref value): + SSANode(parent, VOID, SSA_RET), _value(value) {} + + ref SSARet::value() const { + return _value; + } + + ref& SSARet::value() { + return _value; + } + + Location SSARet::emit(Function& fn) { + return fn.add(new RetInsn(_value->emit(fn))); + } + + void SSARet::format(stream& io) const { + write(io, "return ", _value->id()); + } + + SSAGoto::SSAGoto(ref parent, ref target): + SSANode(parent, VOID, SSA_GOTO), _target(target) {} + + ref SSAGoto::target() const { + return _target; + } + + ref& SSAGoto::target() { + return _target; + } + + Location SSAGoto::emit(Function& fn) { + return fn.add(new GotoInsn(symbol_value(_target->label()))); // todo: bb labels + } + + void SSAGoto::format(stream& io) const { + write(io, "goto ", _target->label()); + } + + SSAIf::SSAIf(ref parent, ref cond, ref if_true, ref if_false): + SSANode(parent, VOID, SSA_IF), _cond(cond), _if_true(if_true), _if_false(if_false) {} + + ref SSAIf::cond() const { + return _cond; + } + + ref& SSAIf::cond() { + return _cond; + } + + ref SSAIf::if_true() const { + return _if_true; + } + + ref& SSAIf::if_true() { + return _if_true; + } + + ref SSAIf::if_false() const { + return _if_false; + } + + ref& SSAIf::if_false() { + return _if_false; + } + + Location SSAIf::emit(Function& fn) { + return loc_none(); + } + + void SSAIf::format(stream& io) const { + write(io, "if ", _cond->id(), " then ", _if_true->label(), " else ", _if_false->label()); + } + + SSAPhi::SSAPhi(ref parent, const Type* type, ref left, ref right, + ref left_block, ref right_block): + SSANode(parent, type, SSA_PHI), _left(left), _right(right), + _left_block(left_block), _right_block(right_block) {} + + ref SSAPhi::left() const { + return _left; + } + + ref& SSAPhi::left() { + return _left; + } + + ref SSAPhi::right() const { + return _right; + } + + ref& SSAPhi::right() { + return _right; + } + + ref SSAPhi::left_block() const { + return _left_block; + } + + ref& SSAPhi::left_block() { + return _left_block; + } + + ref SSAPhi::right_block() const { + return _right_block; + } + + ref& SSAPhi::right_block() { + return _right_block; + } + + Location SSAPhi::emit(Function& fn) { + return loc_none(); + } + + void SSAPhi::format(stream& io) const { + write(io, id(), " = phi ", _left, "(", _left_block->label(), ") ", _right, "(", _right_block->label(), ")"); + } +} + +void write(stream& io, const basil::SSAIdent& id) { + write(io, basil::id_names[id.base], '#', id.count); +} + +void write(stream& io, const ref node) { + node->format(io); +} \ No newline at end of file diff --git a/compiler/ssa.h b/compiler/ssa.h new file mode 100644 index 0000000..c2e20e5 --- /dev/null +++ b/compiler/ssa.h @@ -0,0 +1,284 @@ +#ifndef BASIL_SSA_H +#define BASIL_SSA_H + +#include "util/defs.h" +#include "util/vec.h" +#include "util/rc.h" +#include "ir.h" + +namespace basil { + class Def; + class Env; + + class BasicBlock; + + struct SSAIdent { + u64 base, count; + }; + + // maj min kind + // 000 00 000 + enum SSAKind : u8 { + SSA_BINARY = 0, + SSA_BINARY_MATH = SSA_BINARY, + SSA_ADD = SSA_BINARY_MATH, + SSA_SUB = SSA_BINARY_MATH + 1, + SSA_MUL = SSA_BINARY_MATH + 2, + SSA_DIV = SSA_BINARY_MATH + 3, + SSA_REM = SSA_BINARY_MATH + 4, + SSA_BINARY_LOGIC = SSA_BINARY + 8, + SSA_AND = SSA_BINARY_LOGIC, + SSA_OR = SSA_BINARY_LOGIC + 1, + SSA_XOR = SSA_BINARY_LOGIC + 2, + SSA_BINARY_COMPARE = SSA_BINARY + 16, + SSA_EQ = SSA_BINARY_COMPARE, + SSA_NOT_EQ = SSA_BINARY_COMPARE + 1, + SSA_LESS = SSA_BINARY_COMPARE + 2, + SSA_LESS_EQ = SSA_BINARY_COMPARE + 3, + SSA_GREATER = SSA_BINARY_COMPARE + 4, + SSA_GREATER_EQ = SSA_BINARY_COMPARE + 5, + SSA_UNARY = 32, + SSA_UNARY_MATH = SSA_UNARY, + SSA_UNARY_LOGIC = SSA_UNARY + 8, + SSA_NOT = SSA_UNARY_LOGIC, + SSA_CONSTANT = 64, + SSA_INT = SSA_CONSTANT, + SSA_BOOL = SSA_CONSTANT + 1, + SSA_STRING = SSA_CONSTANT + 2, + SSA_VOID = SSA_CONSTANT + 3, + SSA_SYMBOL = SSA_CONSTANT + 4, + SSA_FUNCTION = SSA_CONSTANT + 5, + SSA_OTHER = 96, + SSA_LOAD = SSA_OTHER, + SSA_STORE = SSA_OTHER + 1, + SSA_CALL = SSA_OTHER + 2, + SSA_RET = SSA_OTHER + 3, + SSA_IF = SSA_OTHER + 4, + SSA_GOTO = SSA_OTHER + 5, + SSA_PHI = SSA_OTHER + 6, + SSA_BB = SSA_OTHER + 7, + SSA_LOAD_LABEL = SSA_OTHER + 8, + }; + + extern const u8 MAJOR_MASK; + extern const u8 MINOR_MASK; + + class SSANode : public RC { + ref _parent; + const Type* _type; + SSAKind _kind; + SSAIdent _id; + public: + SSANode(ref parent, const Type* type, SSAKind kind, const string& name = "."); + + bool is(SSAKind kind) const; + ref parent() const; + ref& parent(); + SSAIdent id() const; + SSAKind kind() const; + const Type* type() const; + virtual Location emit(Function& fn) = 0; + virtual void format(stream& io) const = 0; + }; + + class BasicBlock { + string _label; + vector> _members; + vector> _pred, _succ; + public: + BasicBlock(const string& label = ""); + + const string& label() const; + u32 size() const; + const ref* begin() const; + ref* begin(); + const ref* end() const; + ref* end(); + void push(ref SSANode); + const vector>& members() const; + vector>& members(); + Location emit(Function& fn); + void format(stream& io) const; + }; + + class SSAInt : public SSANode { + i64 _value; + public: + SSAInt(ref parent, i64 value); + + i64 value() const; + i64& value(); + Location emit(Function& fn) override; + void format(stream& io) const override; + }; + + class SSABool : public SSANode { + bool _value; + public: + SSABool(ref parent, bool value); + + bool value() const; + bool& value(); + Location emit(Function& fn) override; + void format(stream& io) const override; + }; + + class SSAString : public SSANode { + string _value; + public: + SSAString(ref parent, const string& value); + + const string& value() const; + string& value(); + Location emit(Function& fn) override; + void format(stream& io) const override; + }; + + class SSAVoid : public SSANode { + public: + SSAVoid(ref parent); + + Location emit(Function& fn) override; + void format(stream& io) const override; + }; + + class SSASymbol : public SSANode { + u64 _value; + public: + SSASymbol(ref parent, u64 value); + + u64 value() const; + u64& value(); + Location emit(Function& fn) override; + void format(stream& io) const override; + }; + + class SSAFunction : public SSANode { + u64 _name; + public: + SSAFunction(ref parent, const Type* type, u64 name); + + Location emit(Function& fn) override; + void format(stream& io) const override; + }; + + class SSALoadLabel : public SSANode { + u64 _name; + public: + SSALoadLabel(ref parent, const Type* type, u64 name); + + Location emit(Function& fn) override; + void format(stream& io) const override; + }; + + class SSAStore : public SSANode { + ref _env; + ref _value; + public: + SSAStore(ref parent, ref env, u64 name, ref value); + + Location emit(Function& fn) override; + void format(stream& io) const override; + }; + + class SSABinary : public SSANode { + ref _left, _right; + public: + SSABinary(ref parent, const Type* type, SSAKind kind, ref left, ref right); + + ref left() const; + ref& left(); + ref right() const; + ref& right(); + Location emit(Function& fn) override; + void format(stream& io) const override; + }; + + class SSAUnary : public SSANode { + ref _child; + public: + SSAUnary(ref parent, const Type* type, SSAKind kind, ref child); + + ref child() const; + ref& child(); + Location emit(Function& fn) override; + void format(stream& io) const override; + }; + + class SSACall : public SSANode { + ref _fn; + vector> _args; + + public: + SSACall(ref parent, const Type* rettype, ref fn, const vector>& args); + + ref fn() const; + ref& fn(); + const vector>& args() const; + vector>& args(); + Location emit(Function& fn) override; + void format(stream& io) const override; + }; + + class SSARet : public SSANode { + ref _value; + public: + SSARet(ref parent, ref value); + + ref value() const; + ref& value(); + Location emit(Function& fn) override; + void format(stream& io) const override; + }; + + class SSAGoto : public SSANode { + ref _target; + public: + SSAGoto(ref parent, ref target); + + ref target() const; + ref& target(); + Location emit(Function& fn) override; + void format(stream& io) const override; + }; + + class SSAIf : public SSANode { + ref _cond; + ref _if_true, _if_false; + public: + SSAIf(ref parent, ref cond, ref if_true, ref if_false); + + ref cond() const; + ref& cond(); + ref if_true() const; + ref& if_true(); + ref if_false() const; + ref& if_false(); + Location emit(Function& fn) override; + void format(stream& io) const override; + }; + + class SSAPhi : public SSANode { + ref _left, _right; + ref _left_block, _right_block; + public: + SSAPhi(ref parent, const Type* type, ref left, ref right, + ref left_block, ref right_block); + + ref left() const; + ref& left(); + ref right() const; + ref& right(); + ref left_block() const; + ref& left_block(); + ref right_block() const; + ref& right_block(); + Location emit(Function& fn) override; + void format(stream& io) const override; + }; +} + +void write(stream& io, const basil::SSAIdent& id); +void write(stream& io, const ref node); + +#endif \ No newline at end of file diff --git a/type.cpp b/compiler/type.cpp similarity index 74% rename from type.cpp rename to compiler/type.cpp index ecebe3d..56536d6 100644 --- a/type.cpp +++ b/compiler/type.cpp @@ -15,6 +15,12 @@ namespace basil { const Type* Type::concretify() const { return this; } + + bool Type::coerces_to(const Type& other) const { + return other == *this + || &other == ANY + || other.kind() == KIND_SUM && ((const SumType&)other).has(this); + } SingletonType::SingletonType(const string& repr) : Type(::hash(repr)), _repr(repr) {} @@ -38,7 +44,7 @@ namespace basil { } const Type* ListType::concretify() const { - return find(_element->concretify()); + return find(_element->concretify()); } const Type* ListType::element() const { @@ -54,9 +60,78 @@ namespace basil { ((const ListType&) other).element() == element(); } + bool ListType::coerces_to(const Type& other) const { + return Type::coerces_to(other) || &other == TYPE && _element == TYPE; + } + void ListType::format(stream& io) const { write(io, "[", _element, "]"); } + + // ArrayType + + ArrayType::ArrayType(const Type* element): + Type(element->hash() ^ 11745103813974897731ul), + _element(element), _fixed(false), _size(0) {} + + ArrayType::ArrayType(const Type* element, u32 size): + Type(element->hash() ^ 5095961086520768179ul * ::hash(size)), + _element(element), _fixed(true), _size(size) {} + + const Type* ArrayType::element() const { + return _element; + } + + u32 ArrayType::count() const { + return _size; + } + + bool ArrayType::fixed() const { + return _fixed; + } + + bool ArrayType::concrete() const { + return _element->concrete(); + } + + const Type* ArrayType::concretify() const { + return _fixed ? find(_element->concretify(), _size) + : find(_element->concretify()); + } + + TypeKind ArrayType::kind() const { + return KIND_ARRAY; + } + + bool ArrayType::operator==(const Type& other) const { + return other.kind() == kind() && + ((const ArrayType&) other).element() == element() && + ((const ArrayType&) other).fixed() == fixed() && + (((const ArrayType&) other).count() == count() || !fixed()); + } + + bool ArrayType::coerces_to(const Type& other) const { + if (Type::coerces_to(other)) return true; + + if (other.kind() == KIND_ARRAY + && ((const ArrayType&)other).element() == element() + && !((const ArrayType&)other).fixed()) return true; + + if (fixed() && other.kind() == KIND_PRODUCT + && ((const ProductType&)other).count() == count()) { + for (u32 i = 0; i < count(); i ++) + if (((const ProductType&)other).member(i) != element()) return false; + return true; + } + + return false; + } + + void ArrayType::format(stream& io) const { + if (_fixed) write(io, _element, "[", _size, "]"); + else write(io, _element, "[]"); + } + u64 set_hash(const set& members) { u64 h = 6530804687830202173ul; @@ -82,6 +157,18 @@ namespace basil { return true; } + bool SumType::coerces_to(const Type& other) const { + if (Type::coerces_to(other)) return true; + + if (other.kind() == KIND_SUM) { + for (const Type* t : _members) + if (!((const SumType&)other).has(t)) return false; + return true; + } + + return false; + } + void SumType::format(stream& io) const { write(io, "("); bool first = true; @@ -134,6 +221,29 @@ namespace basil { return true; } + bool ProductType::coerces_to(const Type& other) const { + if (Type::coerces_to(other)) return true; + + // if (other.kind() == KIND_ARRAY + // && ((const ArrayType&)other).element() == element() + // && !((const ArrayType&)other).fixed()) return true; + + if (&other == TYPE) { + for (u32 i = 0; i < count(); i ++) + if (member(i) != TYPE) return false; + return true; + } + + if (other.kind() == KIND_ARRAY && ((const ArrayType&)other).fixed() + && ((const ArrayType&)other).count() == count()) { + for (u32 i = 0; i < count(); i ++) + if (member(i) != ((const ArrayType&)other).element()) return false; + return true; + } + + return false; + } + void ProductType::format(stream& io) const { write(io, "("); bool first = true; @@ -324,7 +434,7 @@ namespace basil { *ANY = find("any"), *STRING = find("string"); - const Type* unify(const Type* a, const Type* b) { + const Type* unify(const Type* a, const Type* b, bool coercing, bool converting) { if (!a || !b) return nullptr; if (a == ANY) return b; @@ -383,6 +493,11 @@ namespace basil { if (a == VOID && b->kind() == KIND_LIST) return b; if (b == VOID && a->kind() == KIND_LIST) return a; + + if (coercing || converting) { + if (a->coerces_to(*b)) return b; + if (b->coerces_to(*a)) return a; + } if (a != b) return nullptr; return a; diff --git a/type.h b/compiler/type.h similarity index 80% rename from type.h rename to compiler/type.h index 8a7a5f9..34c8ac4 100644 --- a/type.h +++ b/compiler/type.h @@ -16,10 +16,11 @@ namespace basil { KIND_LIST = GC_KIND_FLAG | 0, KIND_SUM = GC_KIND_FLAG | 1, KIND_PRODUCT = GC_KIND_FLAG | 2, - KIND_FUNCTION = GC_KIND_FLAG | 3, - KIND_ALIAS = GC_KIND_FLAG | 4, - KIND_MACRO = GC_KIND_FLAG | 5, - KIND_RUNTIME = GC_KIND_FLAG | 6 + KIND_ARRAY = GC_KIND_FLAG | 3, + KIND_FUNCTION = GC_KIND_FLAG | 4, + KIND_ALIAS = GC_KIND_FLAG | 5, + KIND_MACRO = GC_KIND_FLAG | 6, + KIND_RUNTIME = GC_KIND_FLAG | 7 }; class Type { @@ -33,6 +34,7 @@ namespace basil { virtual bool concrete() const; virtual const Type* concretify() const; virtual bool operator==(const Type& other) const = 0; + virtual bool coerces_to(const Type& other) const; virtual void format(stream& io) const = 0; }; @@ -56,6 +58,26 @@ namespace basil { const Type* concretify() const override; TypeKind kind() const override; bool operator==(const Type& other) const override; + bool coerces_to(const Type& other) const override; + void format(stream& io) const override; + }; + + class ArrayType : public Type { + const Type* _element; + u32 _size; + bool _fixed; + public: + ArrayType(const Type* element); + ArrayType(const Type* element, u32 size); + + const Type* element() const; + u32 count() const; + bool fixed() const; + bool concrete() const override; + const Type* concretify() const override; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + bool coerces_to(const Type& other) const override; void format(stream& io) const override; }; @@ -67,6 +89,7 @@ namespace basil { bool has(const Type* member) const; TypeKind kind() const override; bool operator==(const Type& other) const override; + bool coerces_to(const Type& other) const override; void format(stream& io) const override; }; @@ -81,6 +104,7 @@ namespace basil { const Type* concretify() const override; TypeKind kind() const override; bool operator==(const Type& other) const override; + bool coerces_to(const Type& other) const override; void format(stream& io) const override; }; @@ -163,7 +187,7 @@ namespace basil { extern const Type *INT, *SYMBOL, *VOID, *ERROR, *TYPE, *ALIAS, *BOOL, *ANY, *STRING; - const Type* unify(const Type* a, const Type* b); + const Type* unify(const Type* a, const Type* b, bool coercing = false, bool converting = false); } template<> diff --git a/values.cpp b/compiler/values.cpp similarity index 81% rename from values.cpp rename to compiler/values.cpp index 5fa88c8..722a5f1 100644 --- a/values.cpp +++ b/compiler/values.cpp @@ -62,8 +62,16 @@ namespace basil { _data.rc = p; } + Value::Value(ArrayValue* a) { + set ts; + for (const Value& v : *a) ts.insert(v.type()); + _type = find(find(ts), a->size()); + _data.rc = a; + } + Value::Value(FunctionValue* f): - _type(find(find(), find())) { + _type(f->is_builtin() ? find(ANY, ANY) + : find(find(), find())) { _data.rc = f; } @@ -181,6 +189,18 @@ namespace basil { return *(ListValue*)_data.rc; } + bool Value::is_array() const { + return _type->kind() == KIND_ARRAY; + } + + const ArrayValue& Value::get_array() const { + return *(const ArrayValue*)_data.rc; + } + + ArrayValue& Value::get_array() { + return *(ArrayValue*)_data.rc; + } + bool Value::is_sum() const { return _type->kind() == KIND_SUM; } @@ -245,12 +265,12 @@ namespace basil { return _type->kind() == KIND_RUNTIME; } - const ASTNode* Value::get_runtime() const { + ASTNode* Value::get_runtime() const { return (ASTNode*)_data.rc; } - ASTNode* Value::get_runtime() { - return (ASTNode*)_data.rc; + ASTNode*& Value::get_runtime() { + return (ASTNode*&)_data.rc; } const Type* Value::type() const { @@ -276,6 +296,15 @@ namespace basil { } write(io, ")"); } + else if (is_array()) { + bool first = true; + write(io, "["); + for (const Value& v : get_array()) { + write(io, first ? "" : ", ", v); + first = false; + } + write(io, "]"); + } else if (is_sum()) write(io, get_sum().value()); else if (is_product()) { bool first = true; @@ -320,6 +349,11 @@ namespace basil { for (const Value& v : get_product()) h ^= v.hash(); return h; } + else if (is_array()) { + u64 h = 7135911592309895053ul; + for (const Value& v : get_array()) h ^= v.hash(); + return h; + } else if (is_function()) { u64 h = 10916307465547805281ul; if (get_function().is_builtin()) @@ -356,6 +390,18 @@ namespace basil { else if (is_type()) return get_type() == other.get_type(); else if (is_bool()) return get_bool() == other.get_bool(); else if (is_string()) return get_string() == other.get_string(); + else if (is_product()) { + if (get_product().size() != other.get_product().size()) return false; + for (u32 i = 0; i < get_product().size(); i ++) + if (get_product()[i] != other.get_product()[i]) return false; + return true; + } + else if (is_array()) { + if (get_array().size() != other.get_array().size()) return false; + for (u32 i = 0; i < get_array().size(); i ++) + if (get_array()[i] != other.get_array()[i]) return false; + return true; + } else if (is_list()) { const Value* l = this, *o = &other; while (l->is_list() && o->is_list()) { @@ -411,6 +457,11 @@ namespace basil { for (const Value& v : get_product()) values.push(v); return Value(new ProductValue(values)); } + else if (is_array()) { + vector values; + for (const Value& v : get_array()) values.push(v); + return Value(new ProductValue(values)); + } else if (is_function()) { if (get_function().is_builtin()) { return Value(new FunctionValue(get_function().get_env()->clone(), @@ -523,6 +574,13 @@ namespace basil { return _values.end(); } + const vector& ProductValue::values() const { + return _values; + } + + ArrayValue::ArrayValue(const vector& values): + ProductValue(values) {} + const u64 KEYWORD_ARG_BIT = 1ul << 63; const u64 ARG_NAME_MASK = ~KEYWORD_ARG_BIT; @@ -749,7 +807,7 @@ namespace basil { lhs.type(), "'."); return error(); } - if (!rhs.is_int() && !lhs.is_error()) { + if (!rhs.is_int() && !rhs.is_error()) { err(rhs.loc(), "Expected integer value in arithmetic expression, found '", rhs.type(), "'."); return error(); @@ -799,7 +857,7 @@ namespace basil { lhs.type(), "'."); return error(); } - if (!rhs.is_bool() && !lhs.is_error()) { + if (!rhs.is_bool() && !rhs.is_error()) { err(rhs.loc(), "Expected boolean value in logical expression, found '", rhs.type(), "'."); return error(); @@ -865,7 +923,7 @@ namespace basil { lhs.type(), "'."); return error(); } - if (!rhs.is_int() && !rhs.is_string() && !lhs.is_error()) { + if (!rhs.is_int() && !rhs.is_string() && !rhs.is_error()) { err(rhs.loc(), "Expected integer or string value in relational expression, found '", rhs.type(), "'."); return error(); @@ -981,42 +1039,191 @@ namespace basil { if (val.is_runtime()) return new ASTLength(val.loc(), lower(val).get_runtime()); - - if (!val.is_string() && !val.is_list()) { - err(val.loc(), "Expected string or list, given '", val.type(), "'."); + + if (val.is_string()) return Value(i64(val.get_string().size())); + else if (val.is_list()) return Value(i64(to_vector(val).size())); + else if (val.is_product()) return Value(i64(val.get_product().size())); + else if (val.is_array()) return Value(i64(val.get_array().size())); + else { + err(val.loc(), "Cannot get length of value of type '", val.type(), "'."); return error(); } + } - if (val.is_string()) return Value(i64(val.get_string().size())); - else return Value(i64(to_vector(val).size())); + Value tuple_of(const vector& elements) { + for (const Value& v : elements) { + if (v.is_runtime()) { + err(v.loc(), "Cannot compile tuples yet."); + return error(); + } + } + return Value(new ProductValue(elements)); + } + + Value array_of(const vector& elements) { + for (const Value& v : elements) { + if (v.is_runtime()) { + err(v.loc(), "Cannot compile arrays yet."); + return error(); + } + } + return Value(new ArrayValue(elements)); } - Value char_at(const Value& str, const Value& idx) { - if (str.is_runtime() || idx.is_runtime()) { + Value at(const Value& val, const Value& idx) { + if (val.is_error() || idx.is_error()) return error(); + if (val.is_runtime() || idx.is_runtime()) { vector args; - Value s = lower(str), i = lower(idx); + Value s = lower(val), i = lower(idx); args.push(s.get_runtime()); args.push(i.get_runtime()); + vector arg_types; + arg_types.push(args[0]->type()); + arg_types.push(args[1]->type()); + if (args[0]->type() == STRING) { + return new ASTNativeCall(val.loc(), "_char_at", INT, args, arg_types); + } + else { + err(val.loc(), "Accesses not implemented in AST yet."); + return error(); + } + } + if (!idx.is_int()) { + err(idx.loc(), "Expected integer index in accessor, given '", val.type(), "'."); + return error(); + } + if (val.is_string()) return Value(i64(val.get_string()[idx.get_int()])); + else if (val.is_product()) return val.get_product()[idx.get_int()]; + else if (val.is_array()) return val.get_array()[idx.get_int()]; + else { + err(val.loc(), "Cannot index into value of type '", val.type(), "'."); + return error(); + } + } + + Value strcat(const Value& a, const Value &b) { + if (a.is_error() || b.is_error()) return error(); + if (a.is_runtime() || b.is_runtime()) { + vector args; + Value al = lower(a), bl = lower(b); + args.push(al.get_runtime()); + args.push(bl.get_runtime()); vector arg_types; arg_types.push(STRING); - arg_types.push(INT); - return new ASTNativeCall(str.loc(), "_char_at", INT, args, arg_types); + arg_types.push(STRING); + return new ASTNativeCall(a.loc(), "_strcat", STRING, args, arg_types); } - if (!str.is_string()) { - err(str.loc(), "Expected string, given '", str.type(), "'."); + if (!a.is_string() || !b.is_string()) { + err(a.loc(), "Expected string and string, given '", a.type(), "' and '", b.type(), "'."); return error(); } - if (!idx.is_int()) { - err(idx.loc(), "Expected integer to index string, given '", str.type(), "'."); + return Value(a.get_string() + b.get_string(), STRING); + } + + Value substr(const Value& str, const Value &start, const Value &end) { + if (str.is_error() || start.is_error() || end.is_error()) return error(); + if (str.is_runtime() || start.is_runtime() || end.is_runtime()) { + vector args; + Value strl = lower(str), startl = lower(start), endl = lower(end); + args.push(strl.get_runtime()); + args.push(startl.get_runtime()); + args.push(endl.get_runtime()); + vector arg_types; + arg_types.push(STRING); + arg_types.push(INT); + arg_types.push(INT); + return new ASTNativeCall(str.loc(), "_substr", STRING, args, arg_types); + } + if (!str.is_string() || !start.is_int() || !end.is_int()) { + err(str.loc(), "Expected string, integer, and integer, given '", str.type(), "' and '", start.type(), "' and '", end.type(), "'."); return error(); } - return Value(i64(str.get_string()[idx.get_int()])); + + return end.get_int() < start.get_int() + ? Value("", STRING) + : Value(string(str.get_string()[{start.get_int(), end.get_int()}]), STRING); } Value type_of(const Value& v) { return Value(v.type(), TYPE); } + Value cast(const Value& val, const Type* type) { + if (val.is_product()) { + if (type == TYPE) { + vector ts; + for (const Value& v : val.get_product()) + ts.push(v.get_type()); + return Value(find(ts), TYPE); + } + } + + if (val.is_list() && type == TYPE) { + if (length(val) != 1) { + err(val.loc(), "Only single-element lists can be treated as types."); + return error(); + } + return find(head(val).get_type()); + } + + if (val.is_sum() && val.get_sum().value().type() == type) + return val.get_sum().value(); + else { + err(val.loc(), "Sum value does not currently contain value of type '", type, "'."); + return error(); + } + + if (type->kind() == KIND_SUM) { + return Value(new SumValue(val), val.type()); + } + + err(val.loc(), "Could not convert value to type '", type, "'."); + } + + Value is(const Value& val, const Value& type) { + if (!type.is_type()) { + err(type.loc(), "Expected type value in is-expression, given '", type.type(), "'."); + return error(); + } + if (val.type() == type.get_type()) + return Value(true, BOOL); + else if (val.is_sum() && val.get_sum().value().type() == type.get_type()) + return Value(true, BOOL); + else return Value(false, BOOL); + } + + Value as(const Value& val, const Value& type) { + if (!type.is_type()) { + err(type.loc(), "Expected type value in explicit cast, given '", type.type(), "'."); + return error(); + } + // if (val.is_runtime()) { + // return Value(new ASTAnnotate(val.loc(), val.get_runtime(), type.get_type())); + // } + // else if (val.type()->coerces_to(*type.get_type())) { + // err(val.loc(), "Could not unify value of type '", val.type(), "' with type '", + // type.get_type(), "'."); + // return error(); + // } + return cast(val, type.get_type()); + } + + Value annotate(const Value& val, const Value& type) { + if (!type.is_type()) { + err(type.loc(), "Expected type value in annotation, given '", type.type(), "'."); + return error(); + } + if (val.is_runtime()) { + return Value(new ASTAnnotate(val.loc(), val.get_runtime(), type.get_type())); + } + else if (val.type()->coerces_to(*type.get_type())) { + err(val.loc(), "Could not unify value of type '", val.type(), "' with type '", + type.get_type(), "'."); + return error(); + } + return cast(val, type.get_type()); + } + ASTNode* instantiate(SourceLocation loc, FunctionValue& fn, const Type* args_type) { ref new_env = fn.get_env()->clone(); @@ -1033,6 +1240,7 @@ namespace basil { } } Value cloned = fn.body().clone(); + prep(new_env, cloned); Value v = eval(new_env, cloned); if (v.is_error()) return nullptr; if (!v.is_runtime()) v = lower(v); @@ -1226,7 +1434,9 @@ namespace basil { env->find(argname)->value = arg.get_product()[i]; } } - return eval(env, fn.body()); + Value prepped = fn.body().clone(); + prep(env, prepped); + return eval(env, prepped); } } diff --git a/values.h b/compiler/values.h similarity index 86% rename from values.h rename to compiler/values.h index c22f1c6..1cd48ce 100644 --- a/values.h +++ b/compiler/values.h @@ -20,6 +20,7 @@ namespace basil { class ListValue; class SumValue; class ProductValue; + class ArrayValue; class FunctionValue; class AliasValue; class MacroValue; @@ -43,6 +44,7 @@ namespace basil { Value(ListValue* l); Value(SumValue* s, const Type* type); Value(ProductValue* p); + Value(ArrayValue* a); Value(FunctionValue* f); Value(AliasValue* f); Value(MacroValue* f); @@ -79,6 +81,10 @@ namespace basil { const ListValue& get_list() const; ListValue& get_list(); + bool is_array() const; + const ArrayValue& get_array() const; + ArrayValue& get_array(); + bool is_sum() const; const SumValue& get_sum() const; SumValue& get_sum(); @@ -100,8 +106,8 @@ namespace basil { MacroValue& get_macro(); bool is_runtime() const; - const ASTNode* get_runtime() const; - ASTNode* get_runtime(); + ASTNode* get_runtime() const; + ASTNode*& get_runtime(); const Type* type() const; void format(stream& io) const; @@ -155,6 +161,12 @@ namespace basil { const Value* end() const; Value* begin(); Value* end(); + const vector& values() const; + }; + + class ArrayValue : public ProductValue { + public: + ArrayValue(const vector& values); }; using BuiltinFn = Value (*)(ref, const Value& params); @@ -250,30 +262,45 @@ namespace basil { Value tail(const Value& v); Value cons(const Value& head, const Value& tail); Value empty(); - Value list_of(const Value& element); + Value at(const Value& tuple, const Value& index); vector to_vector(const Value& list); Value is_empty(const Value& list); + Value list_of(const Value& element); template Value list_of(const Value& element, Args... args) { return cons(element, list_of(args...)); } - Value list_of(const vector& elements); + template + Value tuple_of(const Value& element, Args... args) { + return tuple_of(vector_of(args...)); + } + Value tuple_of(const vector& elements); + + template + Value array_of(const Value& element, Args... args) { + return array_of(vector_of(args...)); + } + Value array_of(const vector& elements); + Value error(); Value length(const Value& str); Value read_line(); - Value char_at(const Value& str, const Value& idx); + Value strcat(const Value& a, const Value& b); + Value substr(const Value& str, const Value& start, const Value& end); + ASTNode* instantiate(SourceLocation loc, FunctionValue& fn, + const Type* args_type); Value type_of(const Value& v); - + Value is(const Value& v, const Value& t); + Value annotate(const Value& val, const Value& type); + Value as(const Value& v, const Value& t); Value call(ref env, Value& function, const Value& arg); - Value display(const Value& arg); - Value assign(ref env, const Value& dest, const Value& src); } diff --git a/core.cpp b/core.cpp new file mode 100644 index 0000000..6663ccf --- /dev/null +++ b/core.cpp @@ -0,0 +1,139 @@ +#include "util/defs.h" +#include "stdlib.h" +#include "stdio.h" +#include "string.h" + +extern "C" void* _cons(i64 value, void* next) { + void* result = malloc(sizeof(i64) + sizeof(void*)); + *(i64*)result = value; + *((void**)result + 1) = next; + return result; +} + +extern "C" i64 _listlen(void* list) { + u32 size = 0; + while (list) { + list = *((void**)list + 1); + size ++; + } + return size; +} + +extern "C" void _display_int(i64 value) { + printf("%ld\n", value); +} + +static const char** symbol_table; + +extern "C" void _display_symbol(u64 value) { + printf("%s\n", symbol_table[value]); +} + +extern "C" void _display_bool(bool value) { + printf("%s\n", value ? "true" : "false"); +} + +extern "C" void _display_string(const char* value) { + printf("%s\n", value); +} + +extern "C" void _display_int_list(void* value) { + printf("("); + bool first = true; + while (value) { + i64 i = *(i64*)value; + printf("%s%ld", first ? "" : " ", i); + value = *((void**)value + 1); + first = false; + } + printf(")\n"); +} + +extern "C" void _display_bool_list(void* value) { + printf("("); + bool first = true; + while (value) { + u64 i = *(u64*)value; + printf("%s%s", first ? "" : " ", i ? "true" : "false"); + value = *((void**)value + 1); + first = false; + } + printf(")\n"); +} + +extern "C" void _display_symbol_list(void* value) { + printf("("); + bool first = true; + while (value) { + u64 i = *(u64*)value; + printf("%s%s", first ? "" : " ", symbol_table[i]); + value = *((void**)value + 1); + first = false; + } + printf(")\n"); +} + +extern "C" void _display_native_string_list(void* value) { + printf("("); + bool first = true; + while (value) { + const char* i = *(const char**)value; + printf("%s%s%c", first ? "\"" : " \"", i, '"'); + value = *((void**)value + 1); + first = false; + } + printf(")\n"); +} + +extern "C" i64 _strcmp(const char *a, const char *b) { + while (*a && *b && *a == *b) a ++, b ++; + return *(const unsigned char*)a - *(const unsigned char*)b; +} + +extern "C" i64 _strlen(const char *s) { + const char *copy = s; + while (*s++); + return s - copy - 1; +} + +extern "C" const u8* _read_line() { + static char buffer[1024]; + scanf("%s", buffer); + int length = strlen(buffer); + u8* buf = new u8[length + 1]; + for (u32 i = 0; i < length + 1; i ++) buf[i] = buffer[i]; + buf[length] = '\0'; + return buf; +} + +extern "C" i64 _read_int() { + i64 i; + scanf("%ld", &i); + return i; +} + +extern "C" const u8* _read_word() { + return nullptr; +} + +extern "C" u8 _char_at(const char *s, i64 idx) { + return s[idx]; +} + +extern "C" const u8* _strcat(const char* a, const char* b) { + u8* buf = new u8[_strlen(a) + _strlen(b) + 1]; + u32 i = 0; + for (; *a; a ++) buf[i ++] = *a; + for (; *b; b ++) buf[i ++] = *b; + buf[i] = '\0'; + return buf; +} + +extern "C" const u8* _substr(const char *s, i64 start, i64 end) { + if (end < start) return new u8[1]{'\0'}; + u8* buf = new u8[end - start + 1]; + i64 i = start; + for (; i < end; i ++) buf[i - start] = s[i]; + buf[i + 1] = '\0'; + return buf; +} \ No newline at end of file diff --git a/example/fibonacci b/example/fibonacci new file mode 100644 index 0000000000000000000000000000000000000000..327445fd399e7fd09d974f3822b79d9a20a118a4 GIT binary patch literal 4920 zcmeI0ze~eV5XY}+6$PbL6#RiuMGyyzKM-`0Iw%RIgTIzKl++*SU<*x#IyuxOL-!8; z5&j1TJJe0w+;psyxD^V=kP1izqyka_sen{KDj*g3y8^1+R7*`ot2xxO9=&$7QUjt_X^b@c>GkF9*44{f zuDYw+5A_+3-l+$OKC~S|Lx@zns-;uqzOj^juiH*lt<~lb*Bh?uVotT6>lJi0+o3FG zeqLdkDu{Uim(si-0`G3Hvmb$i_1j$E!v!?1Aifvi!^{f-K8k!0>!$;Jin*AFi1R0q z$LKxdN%Y!l7TvG-{5O0VIo&J1SDX(gXW4wtI6OQ{XWYJm$-<}H3Ixbr6SN)eaj@|T6y2UB1o0!wI<~E$C+RjC> s(h1lv@;oTc4PoSThtOq&>w3{rF1RW@r&XC|g2}f(W$)ic-QR+1ebkDai}8dZ-W( zJQVsT_$PSqt|0ygp1l>6q9WexVV&9cCfP`<5)WSH!P{?U-Zwk@zL!(ptk2%Pm9i}0 zk%DQ6y_^?^^b^22!wdiquERJ!vyh3oV}m8MI78!0^J+%OX)=ZPk!gG|^%$>d8Za`C z^5S}qAvm5k3@(^3Bj-^L%}w(?o0bT4a+p zrF&&a0lcAD8mA0VKok%KL;+Di6c7bO0Z~8{5Cud5QD9UBz8^XD#SDyMj z|9oezw_~kmHpVVKc)0j><4M%pPJR3kMUnMxdu!BMk|zp?0-}H@APR^AqJStM3Wx%t zfG8jehywpdf#asE_4qzmt2t}pP$o-vyyyMXWK zle9G5igUj}Vj)ep5mj5`GFxFw1@5D;>#nq0D?wYWfvbX51#YlhS!(;u0Nh%u*$hH8*vr3I6PTRA ziAuzS&y)Kw91~~AvSH9gq_gVxQt-K(L+ba37(Q{6gVrg=_4~gMN3;5UBHG2Cmf# literal 0 HcmV?d00001 diff --git a/example/list-ops.bl b/example/list-ops.bl index aba676d..f2d571e 100644 --- a/example/list-ops.bl +++ b/example/list-ops.bl @@ -9,7 +9,7 @@ def (add x y) x + y def (multiply x y) x * y display "Even number squares from 1 to 20:" -display nums filter even? map (lambda (x) x * x) +display nums filter even? display "" display "Sum of odd numbers from 1 to 20 that aren't divisible by 5:" @@ -30,8 +30,6 @@ def (prime? x) factor = factor + 1 result -display nums filter prime? - display "Product of prime numbers between 1 and 20 (2 * 3 * 5 * 7 * 11 * 13 * 17):" display nums filter prime? reduce multiply display "" \ No newline at end of file diff --git a/jasmine/obj.cpp b/jasmine/obj.cpp index 9077bdb..55327fa 100644 --- a/jasmine/obj.cpp +++ b/jasmine/obj.cpp @@ -1,9 +1,9 @@ #include "obj.h" #include "sym.h" #include "target.h" -#include -#include -#include +#include "stdio.h" +#include "stdlib.h" +#include "string.h" namespace jasmine { Object::Object(Architecture architecture): @@ -39,11 +39,11 @@ namespace jasmine { for (auto& ref : refs) { u8* pos = (u8*)loaded_code + ref.first; u8* sym = (u8*)find(ref.second.symbol); - if (!sym) { - fprintf(stderr, "[ERROR] Could not resolve ref '%s'.\n", - name(ref.second.symbol)); - exit(1); - } + if (!sym) { + fprintf(stderr, "[ERROR] Could not resolve ref '%s'.\n", + name(ref.second.symbol)); + exit(1); + } u8* field = pos + ref.second.field_offset; switch (ref.second.type) { case REL8: @@ -261,8 +261,181 @@ namespace jasmine { void* Object::find(Symbol symbol) const { if (!loaded_code) return nullptr; - auto it = defs.find(symbol); - if (it == defs.end()) return nullptr; + auto it = defs.find(symbol); + if (it == defs.end()) return nullptr; return (u8*)loaded_code + it->second; } + + struct ELFSectionHeader { + u32 type; + u64 flags; + u32 name_index; + byte_buffer* buf; + u32 entry_size; + u32 link = 0, info = 0; + }; + + static const u64 ELF_SHF_WRITE = 0x01, + ELF_SHF_ALLOC = 0x02, + ELF_SHF_EXECINSTR = 0x04, + ELF_SHF_STRINGS = 0x20; + + u16 elf_machine_for(Architecture arch) { + switch (arch) { + case X86: + return 0x03; + case X86_64: + return 0x3e; + case AARCH64: + return 0xb7; + default: + return 0; + } + } + + u8 elf_reloc_for(Architecture arch, RefType type) { + switch (arch) { + case X86_64: switch(type) { + case REL8: + return 15; + case REL16_BE: + case REL16_LE: + return 13; + case REL32_BE: + case REL32_LE: + return 2; + case REL64_BE: + case REL64_LE: + return 24; + case ABS8: + return 14; + case ABS16_BE: + case ABS16_LE: + return 12; + case ABS32_BE: + case ABS32_LE: + return 10; + case ABS64_BE: + case ABS64_LE: + return 1; + default: + return 0; + } + default: + return 0; + } + } + + void Object::writeELF(const char* path) { + FILE* file = fopen(path, "w"); + if (!file) { + fprintf(stderr, "[ERROR] Could not open file '%s'.\n", path); + exit(1); + } + + byte_buffer elf; + elf.write((char)0x7f, 'E', 'L', 'F'); + elf.write(0x02); // ELFCLASS64 + elf.write((EndianOrder)host_order.value == EndianOrder::UTIL_LITTLE_ENDIAN ? + 1 : 2); // endianness + elf.write(1); // EV_CURRENT + for (int i = 7; i < 16; i ++) elf.write('\0'); + + elf.write(1); // relocatable + elf.write(elf_machine_for(arch)); + elf.write(1); // original elf version + elf.write(0); // entry point + elf.write(0); // no program header + elf.write(0x40); // section header starts after elf header + elf.write(0); // no flags + elf.write(0x40); // elf header size + elf.write(0); // phentsize (unused) + elf.write(0); // phnum (unused) + elf.write(0x40); // section header entry size + elf.write(6); // num sections + elf.write(1); // section header strings are section 0 + + byte_buffer strtab, symtab; + strtab.write('\0'); + symtab.write(0); // reserved symbol 0 + symtab.write(0); + symtab.write(0); + map sym_indices; + vector> locals, globals, total; + for (auto& entry : defs) { + if (entry.first.type == LOCAL_SYMBOL) locals.push(entry); + else globals.push(entry); + } + for (auto& entry : refs) { + if (defs.find(entry.second.symbol) == defs.end()) + globals.push({ entry.second.symbol, -1ul }); + } + for (auto& entry : locals) total.push(entry); + for (auto& entry : globals) total.push(entry); + for (u32 i = 0; i < total.size(); i ++) + sym_indices[total[i].first] = i; + for (auto& entry : total) { + u32 ind = strtab.size(); + strtab.write(name(entry.first), strlen(name(entry.first)) + 1); + symtab.write(ind); // name + u8 info = 0; + info |= (entry.first.type == LOCAL_SYMBOL ? 0 : 1) << 4; // binding + info |= 2; // function value assumed...for now + symtab.write(info); + symtab.write('\0'); // padding + symtab.write(entry.second == -1ul ? 0 : 4); // .text section index + symtab.write(entry.second == -1ul ? 0 : entry.second); // address + symtab.write(8); // symbol size = word size? + } + + byte_buffer rel; + for (auto& entry : refs) { + u64 sym = entry.first; + rel.write(sym + entry.second.field_offset); + u64 info = 0; + info |= sym_indices[entry.second.symbol] << 32l; + info |= elf_reloc_for(arch, entry.second.type); + rel.write(info); + } + + byte_buffer shstrtab, shdrs; + vector> sections; + shstrtab.write('\0'); + byte_buffer empty; + sections.push({ "", { 0, 0, 0, &empty, 0 } }); + sections.push({ ".shstrtab", { 3, ELF_SHF_STRINGS, 0, &shstrtab, 0 } }); + sections.push({ ".strtab", { 3, ELF_SHF_STRINGS, 0, &strtab, 0 } }); + sections.push({ ".symtab", { 2, 0, 0, &symtab, 24, 2, locals.size() + 1 } }); + sections.push({ ".text", { 1, ELF_SHF_ALLOC | ELF_SHF_EXECINSTR, 0, &buf, 0 } }); + sections.push({ ".rel.text", { 9, 0, 0, &rel, 16, 3, 4 } }); + sections.push({ ".data", { 1, ELF_SHF_ALLOC | ELF_SHF_WRITE, 0, &empty, 0 } }); + sections.push({ ".bss", { 1, ELF_SHF_ALLOC | ELF_SHF_WRITE, 0, &empty, 0 } }); + for (auto& entry : sections) { + entry.second.name_index = shstrtab.size(); + shstrtab.write((const char*)entry.first.raw(), entry.first.size() + 1); + } + u64 offset = 0x40 + sections.size() * 0x40; // combined size of shdrs and elf header + for (auto& entry : sections) { + shdrs.write(entry.second.name_index); // offset to string + shdrs.write(entry.second.type); + shdrs.write(entry.second.flags); + shdrs.write(0); + shdrs.write(offset); + shdrs.write(entry.second.buf->size()); + offset += entry.second.buf->size(); + shdrs.write(entry.second.link); + shdrs.write(entry.second.info); + shdrs.write(1); + shdrs.write(entry.second.entry_size); + } + + while (shdrs.size()) elf.write(shdrs.read()); + + for (auto& entry : sections) { + while (entry.second.buf->size()) elf.write(entry.second.buf->read()); + } + + while (elf.size()) fputc(elf.read(), file); + fclose(file); + } } \ No newline at end of file diff --git a/jasmine/obj.h b/jasmine/obj.h index 7aa0e1f..a87c3f5 100644 --- a/jasmine/obj.h +++ b/jasmine/obj.h @@ -41,6 +41,7 @@ namespace jasmine { void load(); void write(const char* path); void read(const char* path); + void writeELF(const char* path); Architecture architecture() const; void* find(Symbol symbol) const; diff --git a/jasmine/sym.cpp b/jasmine/sym.cpp index 8e15aa6..ae77490 100644 --- a/jasmine/sym.cpp +++ b/jasmine/sym.cpp @@ -1,5 +1,5 @@ #include "sym.h" -#include +#include "string.h" #include "util/str.h" namespace jasmine { diff --git a/jasmine/sym.h b/jasmine/sym.h index efcef4d..e240ffd 100644 --- a/jasmine/sym.h +++ b/jasmine/sym.h @@ -2,7 +2,6 @@ #define JASMINE_SYMBOL_H #include "utils.h" -#include namespace jasmine { enum SymbolLinkage : u8 { diff --git a/jasmine/utils.cpp b/jasmine/utils.cpp index cfe8d4c..5c69e9a 100644 --- a/jasmine/utils.cpp +++ b/jasmine/utils.cpp @@ -1,5 +1,5 @@ #include "utils.h" -#include +#include "stdlib.h" byte_buffer::byte_buffer(): _start(0), _end(0), _capacity(32), _data(new u8[_capacity]) { diff --git a/jasmine/utils.h b/jasmine/utils.h index b41a084..9197885 100644 --- a/jasmine/utils.h +++ b/jasmine/utils.h @@ -1,7 +1,7 @@ #ifndef JASMINE_UTIL_H #define JASMINE_UTIL_H -#include +#include "stdint.h" #include "../util/defs.h" #include "../util/vec.h" diff --git a/jasmine/x64.cpp b/jasmine/x64.cpp index eaae5ca..fd61491 100644 --- a/jasmine/x64.cpp +++ b/jasmine/x64.cpp @@ -1,6 +1,6 @@ #include "x64.h" -#include -#include +#include "stdio.h" +#include "stdlib.h" namespace x64 { using namespace jasmine; @@ -49,11 +49,11 @@ namespace x64 { } bool is_register(ArgType type) { - return type < 4; + return type >= REGISTER8 && type <= REGISTER64; } bool is_immediate(ArgType type) { - return (type >= 4 && type < 7) || type == IMM_AUTO; + return (type >= IMM8 && type <= IMM64) || type == IMM_AUTO; } bool is_memory(ArgType type) { @@ -64,17 +64,17 @@ namespace x64 { return (type >= LABEL8 && type <= LABEL64) || type == LABEL_AUTO; } - bool is_absolute(ArgType type) { - return (type >= ABSOLUTE8 && type <= ABSOLUTE64) || type == ABSOLUTE_AUTO; - } + bool is_absolute(ArgType type) { + return (type >= ABSOLUTE8 && type <= ABSOLUTE64) || type == ABSOLUTE_AUTO; + } - bool is_rip_relative(ArgType type) { - return (type >= RIPRELATIVE8 && type <= RIPRELATIVE64) || type == RIPRELATIVE_AUTO; - } + bool is_rip_relative(ArgType type) { + return (type >= RIPRELATIVE8 && type <= RIPRELATIVE64) || type == RIPRELATIVE_AUTO; + } - bool is_displacement_only(ArgType type) { - return is_absolute(type) || is_rip_relative(type); - } + bool is_displacement_only(ArgType type) { + return is_absolute(type) || is_rip_relative(type); + } Size operand_size(ArgType type) { if (type >= IMM_AUTO) return AUTO; @@ -129,18 +129,18 @@ namespace x64 { case SCALED_INDEX64: case SCALED_INDEX_AUTO: return arg.data.scaled_index.offset; - case ABSOLUTE8: - case ABSOLUTE16: - case ABSOLUTE32: - case ABSOLUTE64: - case ABSOLUTE_AUTO: - return arg.data.absolute; - case RIPRELATIVE8: - case RIPRELATIVE16: - case RIPRELATIVE32: - case RIPRELATIVE64: - case RIPRELATIVE_AUTO: - return arg.data.rip_relative; + case ABSOLUTE8: + case ABSOLUTE16: + case ABSOLUTE32: + case ABSOLUTE64: + case ABSOLUTE_AUTO: + return arg.data.absolute; + case RIPRELATIVE8: + case RIPRELATIVE16: + case RIPRELATIVE32: + case RIPRELATIVE64: + case RIPRELATIVE_AUTO: + return arg.data.rip_relative; default: return 0; } @@ -167,6 +167,22 @@ namespace x64 { || type == SCALED_INDEX_AUTO; } + bool operator==(const Arg& lhs, const Arg& rhs) { + if (lhs.type != rhs.type) return false; + if (is_register(lhs.type)) return lhs.data.reg == rhs.data.reg; + if (is_immediate(lhs.type)) return lhs.data.imm64 == rhs.data.imm64; + if (is_label(lhs.type)) return lhs.data.label.id == rhs.data.label.id; + if (is_absolute(lhs.type)) return lhs.data.absolute == rhs.data.absolute; + if (is_rip_relative(lhs.type)) return lhs.data.rip_relative == rhs.data.rip_relative; + if (is_scaled_addressing(lhs.type)) return lhs.data.scaled_index.base == rhs.data.scaled_index.base + && lhs.data.scaled_index.index == rhs.data.scaled_index.index + && lhs.data.scaled_index.scale == rhs.data.scaled_index.scale + && lhs.data.scaled_index.offset == rhs.data.scaled_index.offset; + if (is_memory(lhs.type)) return lhs.data.register_offset.base == rhs.data.register_offset.base + && lhs.data.register_offset.offset == rhs.data.register_offset.offset; + return false; + } + RefType relative(Size size) { switch (size) { case BYTE: return REL8; @@ -526,7 +542,17 @@ namespace x64 { } void emitprefix(const Arg& dest, Size size) { + if (size == WORD) target->code().write(0x66); // 16-bit prefix + u8 rex = 0x40; + Register dest_reg = base_register(dest); + + if (is_64bit_register(dest_reg)) rex |= 1; // 64-bit r/m field + if (is_scaled_addressing(dest.type) && + is_64bit_register(dest.data.scaled_index.index)) rex |= 2; // 64-bit SIB index + + if (size == QWORD) rex |= 8; // 64-bit operand size + if (rex > 0x40) target->code().write(rex); } void emitprefix(const Arg& dest, const Arg& src, Size size) { @@ -535,6 +561,11 @@ namespace x64 { Register src_reg = base_register(src); Register dest_reg = base_register(dest); + if (is_memory(src.type)) { + Register temp = src_reg; + src_reg = dest_reg; + dest_reg = temp; + } if (is_64bit_register(dest_reg)) rex |= 1; // 64-bit r/m field if (is_scaled_addressing(dest.type) && @@ -639,14 +670,14 @@ namespace x64 { disp = memory_displacement(dest); if (is_absolute(src.type)) { - sib |= RSP << 3; - sib |= RBP; - has_sib = true; - } + sib |= RSP << 3; + sib |= RBP; + has_sib = true; + } else if (is_scaled_addressing(dest.type)) { sib |= dest.data.scaled_index.scale << 6; - sib |= dest.data.scaled_index.index << 3; - sib |= dest.data.scaled_index.base; + sib |= (dest.data.scaled_index.index & 7) << 3; + sib |= (dest.data.scaled_index.base & 7); has_sib = true; } else if (base_register(dest) == RSP) { @@ -658,15 +689,15 @@ namespace x64 { else if (is_memory(src.type)) { disp = memory_displacement(src); - if (is_absolute(src.type)) { - sib |= RSP << 3; - sib |= RBP; - has_sib = true; - } + if (is_absolute(src.type)) { + sib |= RSP << 3; + sib |= RBP; + has_sib = true; + } else if (is_scaled_addressing(src.type)) { sib |= src.data.scaled_index.scale << 6; - sib |= src.data.scaled_index.index << 3; - sib |= src.data.scaled_index.base; + sib |= (src.data.scaled_index.index & 7) << 3; + sib |= (src.data.scaled_index.base & 7); has_sib = true; } else if (base_register(src) == RSP) { @@ -678,7 +709,7 @@ namespace x64 { else modrm |= 0b11000000; // register-register mod if (disp && !is_displacement_only(src.type) && - !is_displacement_only(dest.type)) { + !is_displacement_only(dest.type)) { if (disp > -129 && disp < 128) modrm |= 0b01000000; // 8-bit offset else if (disp < -0x80000000l || disp > 0x7fffffffl) { fprintf(stderr, "[ERROR] Cannot represent memory offset %lx " @@ -692,21 +723,21 @@ namespace x64 { else if (is_scaled_addressing(src.type)) modrm |= (base_register(dest) & 7) << 3; else if (!is_displacement_only(src.type) && !is_immediate(src.type)) - modrm |= (base_register(src) & 7) << 3; // source register in reg + modrm |= (base_register(src) & 7) << 3; // source register in reg if (is_scaled_addressing(dest.type)) modrm |= RSP; else if (is_scaled_addressing(src.type) || is_absolute(src.type)) modrm |= RSP; - else if (is_rip_relative(src.type)) - modrm |= RBP; + else if (is_rip_relative(src.type)) + modrm |= RBP; else modrm |= (base_register(dest) & 7); // destination register in r/m byte target->code().write(modrm); if (has_sib) target->code().write(sib); - if (is_displacement_only(src.type) || is_displacement_only(dest.type)) { - target->code().write(little_endian((i32)disp)); - } + if (is_displacement_only(src.type) || is_displacement_only(dest.type)) { + target->code().write(little_endian((i32)disp)); + } else if (disp) { if (disp > -129 && disp < 128) target->code().write((i8)disp); else target->code().write(little_endian((i32)disp)); @@ -751,7 +782,7 @@ namespace x64 { if (is_memory(src.type)) opcode += 2; // set next lowest bit for memory source target->code().write(opcode); - if (is_memory(src.type)) emitargs(src, dest, actual_size); + if (is_memory(src.type)) emitargs(src, dest, actual_size); else emitargs(dest, src, actual_size); } } @@ -850,7 +881,7 @@ namespace x64 { if (is_memory(src.type)) opcode += 2; // set next lowest bit for memory source target->code().write(opcode); - if (is_memory(src.type)) emitargs(src, dest, actual_size); + if (is_memory(src.type)) emitargs(src, dest, actual_size); else emitargs(dest, src, actual_size); } } @@ -894,36 +925,36 @@ namespace x64 { u8 opcode = 0xc0; if (src_size != BYTE) { - fprintf(stderr, "[ERROR] Cannot shift by more than -128 - 127, " - "given %ld.\n", val); - exit(1); - } + fprintf(stderr, "[ERROR] Cannot shift by more than -128 - 127, " + "given %ld.\n", val); + exit(1); + } if (dest_size != BYTE) opcode += 1; // use 0xc1 for larger dests if (val == 1) opcode += 0x10; // use 0xd0/d1 for shifts of 1 - target->code().write(opcode); + target->code().write(opcode); emitargs(dest, dest_size, op); - if (val != 1) write_immediate(val, src_size); // immedate at end + if (val != 1) write_immediate(val, src_size); // immedate at end } else if (is_register(shift.type)) { // use normal opcode - if (base_register(shift) != RCX) { - fprintf(stderr, "[ERROR] Cannot shift by register other than CL.\n"); - exit(1); - } + if (base_register(shift) != RCX) { + fprintf(stderr, "[ERROR] Cannot shift by register other than CL.\n"); + exit(1); + } u8 opcode = 0xd2; if (dest_size != BYTE) opcode ++; // set bottom bit for non-8-bit mode target->code().write(opcode); - emitargs(dest, dest_size, op); + emitargs(dest, dest_size, op); } } - void rol(const Arg& dest, const Arg& src, Size size) { + void rol(const Arg& dest, const Arg& src, Size size) { verify_buffer(); verify_args(dest, src); Size dest_size = resolve_size(dest, size); encode_shift(dest, src, dest_size, 0); - } + } void ror(const Arg& dest, const Arg& src, Size size) { verify_buffer(); @@ -931,7 +962,7 @@ namespace x64 { Size dest_size = resolve_size(dest, size); encode_shift(dest, src, dest_size, 1); - } + } void rcl(const Arg& dest, const Arg& src, Size size) { verify_buffer(); @@ -939,7 +970,7 @@ namespace x64 { Size dest_size = resolve_size(dest, size); encode_shift(dest, src, dest_size, 2); - } + } void rcr(const Arg& dest, const Arg& src, Size size) { verify_buffer(); @@ -947,7 +978,7 @@ namespace x64 { Size dest_size = resolve_size(dest, size); encode_shift(dest, src, dest_size, 3); - } + } void shl(const Arg& dest, const Arg& src, Size size) { verify_buffer(); @@ -955,7 +986,7 @@ namespace x64 { Size dest_size = resolve_size(dest, size); encode_shift(dest, src, dest_size, 4); - } + } void shr(const Arg& dest, const Arg& src, Size size) { verify_buffer(); @@ -963,7 +994,7 @@ namespace x64 { Size dest_size = resolve_size(dest, size); encode_shift(dest, src, dest_size, 5); - } + } void sar(const Arg& dest, const Arg& src, Size size) { verify_buffer(); @@ -971,7 +1002,7 @@ namespace x64 { Size dest_size = resolve_size(dest, size); encode_shift(dest, src, dest_size, 7); - } + } void idiv(const Arg& src, Size size) { verify_buffer(); @@ -984,54 +1015,54 @@ namespace x64 { } emitprefix(src, actual_size); - target->code().write(actual_size == BYTE ? 0xf6 : 0xf7); + target->code().write(actual_size == BYTE ? 0xf6 : 0xf7); emitargs(src, actual_size, 7); } - void not_(const Arg& src, Size size) { + void not_(const Arg& src, Size size) { verify_buffer(); Size actual_size = resolve_size(src, size); - if (is_immediate(src.type)) { + if (is_immediate(src.type)) { fprintf(stderr, "[ERROR] Invalid operand; immediate not permitted " "in 'not' instruction.\n"); exit(1); - } + } emitprefix(src, actual_size); - target->code().write(actual_size == BYTE ? 0xf6 : 0xf7); - emitargs(src, actual_size, 2); - } + target->code().write(actual_size == BYTE ? 0xf6 : 0xf7); + emitargs(src, actual_size, 2); + } - void inc(const Arg& src, Size size) { + void inc(const Arg& src, Size size) { verify_buffer(); Size actual_size = resolve_size(src, size); - if (is_immediate(src.type)) { + if (is_immediate(src.type)) { fprintf(stderr, "[ERROR] Invalid operand; immediate not permitted " "in 'inc' instruction.\n"); exit(1); - } + } emitprefix(src, actual_size); - target->code().write(actual_size == BYTE ? 0xfe : 0xff); - emitargs(src, actual_size, 0); - } + target->code().write(actual_size == BYTE ? 0xfe : 0xff); + emitargs(src, actual_size, 0); + } - void dec(const Arg& src, Size size) { + void dec(const Arg& src, Size size) { verify_buffer(); Size actual_size = resolve_size(src, size); - if (is_immediate(src.type)) { + if (is_immediate(src.type)) { fprintf(stderr, "[ERROR] Invalid operand; immediate not permitted " "in 'dec' instruction.\n"); exit(1); - } + } emitprefix(src, actual_size); - target->code().write(actual_size == BYTE ? 0xfe : 0xff); - emitargs(src, actual_size, 1); - } + target->code().write(actual_size == BYTE ? 0xfe : 0xff); + emitargs(src, actual_size, 1); + } void push(const Arg& src, Size size) { verify_buffer(); @@ -1072,34 +1103,34 @@ namespace x64 { } void lea(const Arg& dest, const Arg& src, Size size) { - verify_buffer(); - Size actual_size = resolve_size(dest, src, size); - - emitprefix(dest, src, actual_size); + verify_buffer(); + Size actual_size = resolve_size(dest, src, size); + emitprefix(src, dest, actual_size); if (is_immediate(src.type)) { fprintf(stderr, "[ERROR] Invalid operand; immediate not permitted " "in 'lea' instruction.\n"); exit(1); } - else if (is_register(src.type)) { - fprintf(stderr, "[ERROR] Invalid source operand; register not permitted " - "in 'lea' instruction.\n"); - exit(1); - } + else if (is_register(src.type)) { + fprintf(stderr, "[ERROR] Invalid source operand; register not permitted " + "in 'lea' instruction.\n"); + exit(1); + } else if (is_memory(src.type)) { - if (!is_register(dest.type)) { - fprintf(stderr, "[ERROR] Invalid dest operand; destination in 'lea' " - "instruction must be register.\n"); - } - Arg disp = src; - if (is_label(src.type)) disp = riprel64(0); + if (!is_register(dest.type)) { + fprintf(stderr, "[ERROR] Invalid dest operand; destination in 'lea' " + "instruction must be register.\n"); + } + Arg disp = src; + if (is_label(src.type)) disp = riprel64(0); target->code().write(0x8d); - emitargs(dest, disp, actual_size); - if (is_label(src.type)) + if (is_displacement_only(disp.type)) emitargs(dest, disp, actual_size); + else emitargs(disp, dest, actual_size); + if (is_label(src.type)) target->reference(src.data.label, relative(DWORD), -4); } - } + } void cdq() { verify_buffer(); @@ -1201,28 +1232,29 @@ namespace x64 { if (actual_size <= BYTE) actual_size = WORD; // cannot call by smaller than dword if (is_label(dest.type) || is_immediate(dest.type)) { - if (actual_size > DWORD) actual_size = DWORD; + if (actual_size > DWORD) actual_size = DWORD; if (actual_size <= WORD) actual_size = DWORD; // cannot call by smaller than dword i64 imm = 0; if (is_immediate(dest.type)) imm = dest.data.imm64; - if (imm < -0x8000000l || imm > 0x7fffffffl) { - fprintf(stderr, "[ERROR] Call offset too large; must fit within 32 bits.\n"); - exit(1); - } + if (imm < -0x8000000l || imm > 0x7fffffffl) { + fprintf(stderr, "[ERROR] Call offset too large; must fit within 32 bits.\n"); + exit(1); + } target->code().write(0xe8); // opcode write_immediate(imm, actual_size); if (is_label(dest.type)) target->reference(dest.data.label, relative(actual_size), -4); } else { + emitprefix(dest, actual_size); target->code().write(0xff); emitargs(dest, actual_size, 2); } } - void setcc(const Arg& dest, Condition condition, Size size) { + void setcc(const Arg& dest, Condition condition, Size size) { verify_buffer(); Size actual_size = resolve_size(dest, size); @@ -1231,14 +1263,10 @@ namespace x64 { "in 'setcc' instruction.\n"); exit(1); } - else if (is_memory(dest.type)) { - fprintf(stderr, "[ERROR] Invalid operand; memory operand " - "not permitted in 'setcc' instruction.\n"); - exit(1); - } else { + emitprefix(dest, BYTE); target->code().write(0x0f, 0x90 + (u8)condition); emitargs(dest, actual_size, 0); } - } + } } \ No newline at end of file diff --git a/jasmine/x64.h b/jasmine/x64.h index 1668371..9b316fe 100644 --- a/jasmine/x64.h +++ b/jasmine/x64.h @@ -151,8 +151,10 @@ namespace x64 { ArgType type; }; - - bool is_register(ArgType type); + + bool operator==(const Arg& lhs, const Arg& rhs); + + bool is_register(ArgType type); bool is_immediate(ArgType type); bool is_memory(ArgType type); bool is_label(ArgType type); diff --git a/runtime.cpp b/runtime.cpp new file mode 100644 index 0000000..e69de29 diff --git a/ssa.cpp b/ssa.cpp deleted file mode 100644 index c52ba36..0000000 --- a/ssa.cpp +++ /dev/null @@ -1,713 +0,0 @@ -#include "ssa.h" -#include "util/hash.h" - -namespace basil { - using namespace x64; - - Location ssa_none() { - Location loc; - loc.type = SSA_NONE; - return loc; - } - - Location ssa_immediate(i64 i) { - Location loc; - loc.type = SSA_IMMEDIATE; - loc.immediate = i; - return loc; - } - - Insn::Insn(): - _loc(ssa_none()), _func(nullptr) {} - - Insn::~Insn() { - // - } - - void Insn::setfunc(Function* func) { - _func = func; - } - - Location Insn::loc() { - if (_loc.type == SSA_NONE) _loc = lazy_loc(); - return _loc; - } - - static u32 anonymous_locals = 0; - static u32 anonymous_labels = 0; - static vector all_labels; - static map label_map; - static vector all_locals; - static vector all_constants; - - u32 ssa_find_label(const string& label) { - auto it = label_map.find(label); - if (it == label_map.end()) return ssa_add_label(label); - return it->second; - } - - u32 ssa_add_label(const string& label) { - all_labels.push(label); - label_map[label] = all_labels.size() - 1; - return all_labels.size() - 1; - } - - u32 ssa_next_label() { - buffer b; - write(b, ".L", anonymous_labels ++); - string s; - read(b, s); - all_labels.push(s); - label_map[s] = all_labels.size() - 1; - return all_labels.size() - 1; - } - - Location ssa_next_local(const Type* t) { - buffer b; - write(b, ".t", anonymous_locals ++); - string s; - read(b, s); - all_locals.push({ s, 0, t, {}}); - - Location loc; - loc.type = SSA_LOCAL; - loc.local_index = all_locals.size() - 1; - return loc; - } - - Location ssa_const(u32 label, const string& constant) { - ConstantInfo info; - info.type = STRING; - info.name = all_labels[label]; - for (u32 i = 0; i < constant.size(); i ++) info.data.push(constant[i]); - info.data.push('\0'); - - all_constants.push(info); - Location loc; - loc.type = SSA_CONSTANT; - loc.constant_index = all_constants.size() - 1; - return loc; - } - - Symbol symbol_for_label(u32 label, SymbolLinkage type) { - return type == GLOBAL_SYMBOL ? - global((const char*)all_labels[label].raw()) - : local((const char*)all_labels[label].raw()); - } - - void ssa_emit_constants(Object& object) { - using namespace x64; - writeto(object); - for (const ConstantInfo& info : all_constants) { - label(symbol_for_label(label_map[info.name], GLOBAL_SYMBOL)); - for (u8 b : info.data) object.code().write(b); - } - } - - static map local_state_counts; - - Location ssa_next_local_for(const Location& loc) { - const LocalInfo& info = all_locals[loc.local_index]; - auto it = local_state_counts.find(info.name); - if (it == local_state_counts.end()) { - LocalInfo new_info = { info.name, 0, info.type, info.value }; - all_locals.push(new_info); - Location loc; - loc.type = SSA_LOCAL; - loc.local_index = all_locals.size() - 1; - local_state_counts[info.name] = 1; - return loc; - } - else { - u32 num = it->second; - LocalInfo new_info = info; - new_info.index = num; - all_locals.push(new_info); - Location loc; - loc.type = SSA_LOCAL; - loc.local_index = all_locals.size() - 1; - it->second ++; - return loc; - } - } - - const Type* ssa_type(const Location& loc) { - switch (loc.type) { - case SSA_NONE: - return VOID; - case SSA_LOCAL: - return all_locals[loc.local_index].type; - case SSA_CONSTANT: - return all_constants[loc.constant_index].type; - case SSA_IMMEDIATE: - return INT; // close enough at this stage - case SSA_LABEL: - return INT; // ...close enough :p - } - } - - x64::Arg x64_arg(const Location& loc) { - switch (loc.type) { - case SSA_NONE: - return imm(0); - case SSA_LOCAL: - return all_locals[loc.local_index].value; - case SSA_CONSTANT: - return label64( - global((const char*)all_constants[loc.constant_index].name.raw())); - case SSA_IMMEDIATE: - return imm(loc.immediate); - case SSA_LABEL: - return label64(global((const char*)all_labels[loc.label_index].raw())); - } - } - - Function::Function(u32 label): - _stack(0), _label(label) {} - - Function::Function(const string& label): - _stack(0), _label(ssa_add_label(label)) {} - - Location Function::create_local(const Type* t) { - Location l = ssa_next_local(t); - _locals.push(l); - return l; - } - - Function::~Function() { - for (Insn* i : _insns) delete i; - for (Function* f : _fns) delete f; - } - - void Function::place_label(u32 label) { - _labels[label] = _insns.size(); - } - - Function& Function::create_function() { - _fns.push(new Function(ssa_next_label())); - return *_fns.back(); - } - - Function& Function::create_function(const string& name) { - _fns.push(new Function(name)); - return *_fns.back(); - } - - Location Function::create_local(const string& name, const Type* t) { - LocalInfo info = { name, 0, t, {}}; - all_locals.push(info); - local_state_counts[name] = 1; - - Location loc; - loc.type = SSA_LOCAL; - loc.local_index = all_locals.size() - 1; - _locals.push(loc); - return loc; - } - - Location Function::next_local(const Location& loc) { - Location next = ssa_next_local_for(loc); - _locals.push(next); - return next; - } - - Location Function::add(Insn* insn) { - insn->setfunc(this); - _insns.push(insn); - return insn->loc(); - } - - u32 Function::label() const { - return _label; - } - - void Function::allocate() { - for (Function* fn : _fns) fn->allocate(); - for (Location l : _locals) { - LocalInfo& info = all_locals[l.local_index]; - info.value = x64::m64(RBP, -(_stack += 8)); // assumes everything is a word - } - } - - void Function::emit(Object& obj) { - for (Function* fn : _fns) fn->emit(obj); - - writeto(obj); - Symbol label = global((const char*)all_labels[_label].raw()); - x64::label(label); - push(r64(RBP)); - mov(r64(RBP), r64(RSP)); - sub(r64(RSP), imm(_stack)); - - for (Insn* i : _insns) i->emit(); - - mov(r64(RSP), r64(RBP)); - pop(r64(RBP)); - ret(); - } - - void Function::format(stream& io) const { - for (Function* fn : _fns) fn->format(io); - writeln(io, all_labels[_label], ":"); - for (Insn* i : _insns) writeln(io, " ", i); - } - - Location LoadInsn::lazy_loc() { - return _func->create_local(ssa_type(_src)); - } - - LoadInsn::LoadInsn(Location src): - _src(src) {} - - void LoadInsn::emit() { - auto temp = r64(RAX), src = x64_arg(_src), dst = x64_arg(_loc); - mov(temp, src); - mov(dst, temp); - } - - void LoadInsn::format(stream& io) const { - write(io, _loc, " = ", _src); - } - - Location StoreInsn::lazy_loc() { - if (_init) return _dest; - else return _func->next_local(_dest); - } - - StoreInsn::StoreInsn(Location dest, Location src, bool init): - _dest(dest), _src(src), _init(init) {} - - void StoreInsn::emit() { - auto temp = r64(RAX), src = x64_arg(_src), dst = x64_arg(_dest); - mov(temp, src); - mov(dst, temp); - } - - void StoreInsn::format(stream& io) const { - write(io, _loc, " = ", _src); - } - - LoadPtrInsn::LoadPtrInsn(Location src, const Type* t, i32 offset): - _src(src), _type(t), _offset(offset) {} - - Location LoadPtrInsn::lazy_loc() { - return _func->create_local(_type); - } - - void LoadPtrInsn::emit() { - auto src = x64_arg(_src), dst = x64_arg(_loc); - mov(r64(RAX), src); - mov(r64(RDX), m64(RAX, _offset)); - mov(dst, r64(RDX)); - } - - void LoadPtrInsn::format(stream& io) const { - write(io, _loc, " = *", _src); - } - - StorePtrInsn::StorePtrInsn(Location dest, Location src, i32 offset): - _dest(dest), _src(src), _offset(offset) {} - - Location StorePtrInsn::lazy_loc() { - return ssa_none(); - } - - void StorePtrInsn::emit() { - auto src = x64_arg(_src), dst = x64_arg(_dest); - mov(r64(RAX), dst); - mov(r64(RDX), src); - mov(m64(RAX, _offset), r64(RDX)); - } - - void StorePtrInsn::format(stream& io) const { - write(io, "*", _dest, " = ", _src); - } - - AddressInsn::AddressInsn(Location src, const Type* t): - _src(src), _type(t) {} - - Location AddressInsn::lazy_loc() { - return _func->create_local(_type); - } - - void AddressInsn::emit() { - auto src = x64_arg(_src), dst = x64_arg(_loc); - lea(r64(RAX), src); - mov(dst, r64(RAX)); - } - - void AddressInsn::format(stream& io) const { - write(io, _loc, " = &", _src); - } - - void emit_binary(void(*op)(const x64::Arg&, const x64::Arg&, x64::Size), - Location dst, Location left, Location right, x64::Size size = AUTO) { - auto temp = r64(RAX), _left = x64_arg(left), - _right = x64_arg(right), _dst = x64_arg(dst); - mov(temp, _left); - op(temp, _right, size); - mov(_dst, temp); - } - - void emit_compare(x64::Condition cond, Location dst, - Location left, Location right) { - auto temp = r64(RAX), _left = x64_arg(left), - _right = x64_arg(right), _dst = x64_arg(dst); - mov(temp, _left); - cmp(temp, _right); - if (is_memory(_dst.type)) { - mov(temp, imm(0)); - setcc(temp, cond); - mov(_dst, temp); - } - else { - mov(_dst, imm(0)); - setcc(_dst, cond); - } - } - - BinaryInsn::BinaryInsn(const char* name, Location left, - Location right): - _name(name), _left(left), _right(right) {} - - void BinaryInsn::format(stream& io) const { - write(io, _loc, " = ", _left, " ", _name, " ", _right); - } - - Location BinaryMathInsn::lazy_loc() { - return _func->create_local(ssa_type(_left)); - } - - BinaryMathInsn::BinaryMathInsn(const char* name, Location left, - Location right): - BinaryInsn(name, left, right) {} - - AddInsn::AddInsn(Location left, Location right): - BinaryMathInsn("+", left, right) {} - - void AddInsn::emit() { - emit_binary(add, _loc, _left, _right); - } - - SubInsn::SubInsn(Location left, Location right): - BinaryMathInsn("-", left, right) {} - - void SubInsn::emit() { - emit_binary(sub, _loc, _left, _right); - } - - MulInsn::MulInsn(Location left, Location right): - BinaryMathInsn("*", left, right) {} - - void MulInsn::emit() { - auto temp = r64(RAX), left = x64_arg(_left), - right = x64_arg(_right), dst = x64_arg(_loc); - mov(temp, left); - if (_right.type == SSA_IMMEDIATE) { - mov(r64(RDX), right); - imul(temp, r64(RDX)); - } - else imul(temp, right); - mov(dst, temp); - } - - DivInsn::DivInsn(Location left, Location right): - BinaryMathInsn("/", left, right) {} - - void DivInsn::emit() { - auto rax = r64(RAX), rcx = r64(RCX), rdx = r64(RDX), - left = x64_arg(_left), right = x64_arg(_right), - dst = x64_arg(_loc); - mov(rax, left); - cdq(); - if (_right.type == SSA_IMMEDIATE) { - mov(rcx, right); - idiv(rcx); - } - else idiv(right, QWORD); - mov(dst, rax); - } - - RemInsn::RemInsn(Location left, Location right): - BinaryMathInsn("%", left, right) {} - - void RemInsn::emit() { - auto rax = r64(RAX), rcx = r64(RCX), rdx = r64(RDX), - left = x64_arg(_left), right = x64_arg(_right), - dst = x64_arg(_loc); - mov(rax, left); - cdq(); - if (_right.type == SSA_IMMEDIATE) { - mov(rcx, right); - idiv(rcx); - } - else idiv(right, QWORD); - mov(dst, rdx); - } - - BinaryLogicInsn::BinaryLogicInsn(const char* name, Location left, - Location right): - BinaryInsn(name, left, right) {} - - Location BinaryLogicInsn::lazy_loc() { - return _func->create_local(BOOL); - } - - AndInsn::AndInsn(Location left, Location right): - BinaryLogicInsn("and", left, right) {} - - void AndInsn::emit() { - emit_binary(and_, _loc, _left, _right); - } - - OrInsn::OrInsn(Location left, Location right): - BinaryLogicInsn("or", left, right) {} - - void OrInsn::emit() { - emit_binary(or_, _loc, _left, _right); - } - - XorInsn::XorInsn(Location left, Location right): - BinaryLogicInsn("xor", left, right) {} - - void XorInsn::emit() { - emit_binary(xor_, _loc, _left, _right); - } - - NotInsn::NotInsn(Location src): - _src(src) {} - - Location NotInsn::lazy_loc() { - return _func->create_local(BOOL); - } - - void NotInsn::emit() { - auto src = x64_arg(_src), dst = x64_arg(_loc); - xor_(r64(RDX), r64(RDX)); - mov(r64(RAX), src); - cmp(r64(RAX), imm(0)); - setcc(r64(RDX), ZERO); - mov(dst, r64(RDX)); - } - - void NotInsn::format(stream& io) const { - write(io, _loc, " = ", "not ", _src); - } - - BinaryEqualityInsn::BinaryEqualityInsn(const char* name, - Location left, Location right): - BinaryInsn(name, left, right) {} - - Location BinaryEqualityInsn::lazy_loc() { - return _func->create_local(BOOL); - } - - EqualInsn::EqualInsn(Location left, Location right): - BinaryEqualityInsn("==", left, right) {} - - void EqualInsn::emit() { - emit_compare(EQUAL, _loc, _left, _right); - } - - InequalInsn::InequalInsn(Location left, Location right): - BinaryEqualityInsn("!=", left, right) {} - - void InequalInsn::emit() { - emit_compare(NOT_EQUAL, _loc, _left, _right); - } - - BinaryRelationInsn::BinaryRelationInsn(const char* name, - Location left, Location right): - BinaryInsn(name, left, right) {} - - Location BinaryRelationInsn::lazy_loc() { - return _func->create_local(BOOL); - } - - LessInsn::LessInsn(Location left, Location right): - BinaryRelationInsn("<", left, right) {} - - void LessInsn::emit() { - emit_compare(LESS, _loc, _left, _right); - } - - LessEqualInsn::LessEqualInsn(Location left, Location right): - BinaryRelationInsn("<=", left, right) {} - - void LessEqualInsn::emit() { - emit_compare(LESS_OR_EQUAL, _loc, _left, _right); - } - - GreaterInsn::GreaterInsn(Location left, Location right): - BinaryRelationInsn(">", left, right) {} - - void GreaterInsn::emit() { - emit_compare(GREATER, _loc, _left, _right); - } - - GreaterEqualInsn::GreaterEqualInsn(Location left, Location right): - BinaryRelationInsn(">=", left, right) {} - - void GreaterEqualInsn::emit() { - emit_compare(GREATER_OR_EQUAL, _loc, _left, _right); - } - - Location RetInsn::lazy_loc() { - return ssa_none(); - } - - RetInsn::RetInsn(Location src): - _src(src) {} - - void RetInsn::emit() { - auto rax = r64(RAX), src = x64_arg(_src); - mov(rax, src); - } - - void RetInsn::format(stream& io) const { - write(io, "return ", _src); - } - - static const x64::Register X64_ARG_REGISTERS[] = { - RDI, RSI, RDX, RCX, R8, R9 - }; - - LoadArgumentInsn::LoadArgumentInsn(u32 index, const Type* type): - _index(index), _type(type) {} - - Location LoadArgumentInsn::lazy_loc() { - return _func->create_local(_type); - } - - void LoadArgumentInsn::emit() { - mov(x64_arg(_loc), r64(X64_ARG_REGISTERS[_index])); - } - - void LoadArgumentInsn::format(stream& io) const { - write(io, _loc, " = $", _index); - } - - StoreArgumentInsn::StoreArgumentInsn(Location src, u32 index, const Type* type): - _src(src), _index(index), _type(type) {} - - Location StoreArgumentInsn::lazy_loc() { - return ssa_none(); - } - - void StoreArgumentInsn::emit() { - mov(r64(X64_ARG_REGISTERS[_index]), x64_arg(_src)); - } - - void StoreArgumentInsn::format(stream& io) const { - write(io, "$", _index, " = ", _src); - } - - CallInsn::CallInsn(Location fn, const Type* ret): - _fn(fn), _ret(ret) {} - - Location CallInsn::lazy_loc() { - return _func->create_local(_ret); - } - - void CallInsn::emit() { - if (_fn.type == SSA_LABEL) - call(label64(symbol_for_label(_fn.label_index, GLOBAL_SYMBOL))); - else { - mov(r64(RAX), x64_arg(_fn)); - call(r64(RAX)); - } - mov(x64_arg(_loc), r64(RAX)); - } - - void CallInsn::format(stream& io) const { - write(io, _loc, " = ", _fn, "()"); - } - - Label::Label(u32 label): - _label(label) {} - - Location Label::lazy_loc() { - return ssa_none(); - } - - void Label::emit() { - label(symbol_for_label(_label, LOCAL_SYMBOL)); - } - - void Label::format(stream& io) const { - write(io, "\b\b\b\b", all_labels[_label], ":"); - } - - GotoInsn::GotoInsn(u32 label): - _label(label) {} - - Location GotoInsn::lazy_loc() { - return ssa_none(); - } - - void GotoInsn::emit() { - jmp(label64(symbol_for_label(_label, LOCAL_SYMBOL))); - } - - void GotoInsn::format(stream& io) const { - write(io, "goto ", all_labels[_label]); - } - - IfZeroInsn::IfZeroInsn(u32 label, Location cond): - _label(label), _cond(cond) {} - - Location IfZeroInsn::lazy_loc() { - return ssa_none(); - } - - void IfZeroInsn::emit() { - auto cond = x64_arg(_cond); - if (is_immediate(cond.type)) { - mov(r64(RAX), cond); - cmp(r64(RAX), imm(0)); - } - else cmp(x64_arg(_cond), imm(0)); - jcc(label64(symbol_for_label(_label, LOCAL_SYMBOL)), EQUAL); - } - - void IfZeroInsn::format(stream& io) const { - write(io, "if not ", _cond, " goto ", all_labels[_label]); - } -} - -void write(stream& io, const basil::Location& loc) { - switch (loc.type) { - case basil::SSA_NONE: - write(io, "none"); - return; - case basil::SSA_LOCAL: - write(io, basil::all_locals[loc.local_index].name); - if (basil::all_locals[loc.local_index].index > 0 - || basil::all_locals[loc.local_index].name[0] != '.') - write(io, ".", basil::all_locals[loc.local_index].index); - return; - case basil::SSA_IMMEDIATE: - write(io, loc.immediate); - return; - case basil::SSA_LABEL: - write(io, basil::all_labels[loc.label_index]); - return; - case basil::SSA_CONSTANT: - write(io, basil::all_constants[loc.constant_index].name); - return; - default: - return; - } -} - -void write(stream& io, basil::Insn* insn) { - insn->format(io); -} - -void write(stream& io, const basil::Insn* insn) { - insn->format(io); -} - -void write(stream& io, const basil::Function& func) { - func.format(io); -} \ No newline at end of file diff --git a/std/math.bl b/std/math.bl index 173b603..2d80273 100644 --- a/std/math.bl +++ b/std/math.bl @@ -1,7 +1,7 @@ -infix 45 (x ^ y) - if y < 0 1 / (x ^ -y) - :elif y == 0 1 - :else x * x ^ (y - 1) +# infix 45 (x ^ y) +# if y < 0 1 / (x ^ -y) +# :elif y == 0 1 +# :else x * x ^ (y - 1) infix 45 (x squared) x * x infix 45 (x cubed) x * x * x diff --git a/util/defs.h b/util/defs.h index 2de9706..08920af 100644 --- a/util/defs.h +++ b/util/defs.h @@ -1,7 +1,8 @@ #ifndef BASIL_DEFS_H #define BASIL_DEFS_H -#include +#include "stdint.h" +#include "utils.h" typedef uint8_t u8; typedef uint16_t u16; diff --git a/util/hash.h b/util/hash.h index bfbf49d..c02bb15 100644 --- a/util/hash.h +++ b/util/hash.h @@ -4,7 +4,6 @@ #include "defs.h" #include "str.h" #include "slice.h" -#include template bool equals(const T& a, const T& b) { diff --git a/util/io.cpp b/util/io.cpp index aeef50a..2259735 100644 --- a/util/io.cpp +++ b/util/io.cpp @@ -1,6 +1,5 @@ #include "io.h" #include "str.h" -#include bool exists(const char* path) { FILE* f = fopen(path, "r"); diff --git a/util/io.h b/util/io.h index 6495828..f529efa 100644 --- a/util/io.h +++ b/util/io.h @@ -124,4 +124,13 @@ void read(stream& io, T& t, Args&... args) { read(io, args...); } +template +String format(const Args&... args) { + String s; + buffer b; + write(b, args...); + while (b.peek()) s += b.read(); + return s; +} + #endif \ No newline at end of file diff --git a/util/rc.h b/util/rc.h index acf93d9..ca7a536 100644 --- a/util/rc.h +++ b/util/rc.h @@ -2,6 +2,7 @@ #define BASIL_RC_H #include "defs.h" +#include "stddef.h" class RC { u64 _count; @@ -12,60 +13,72 @@ class RC { void dec(); }; -enum NullType { NULL_VALUE }; - template class ref { - struct BoxedValue { - u64 count; - T data; - }* _value; - - ref(NullType): _value(nullptr) {} public: - static constexpr ref null(); - template - ref(Args... args): _value(new BoxedValue{1, T(args...)}) {} - - ~ref() { - if (_value && !--_value->count) delete _value; + u64* _count; + T* _data; + + ref(): _count(nullptr), _data(nullptr) {} + ref(T* t): _count(new u64(1)), _data(t) {} + ref(decltype(nullptr) null): ref() {} + virtual ~ref() { + if (_data && !--*_count) { + delete _data; + delete _count; + } } - ref(const ref& other): _value(other._value) { - if (other._value) other._value->count ++; + ref(const ref& other): _count(other._count), _data(other._data) { + if (_data) ++ *_count; } ref& operator=(const ref& other) { - if (other._value) other._value->count ++; - if (_value && !--_value->count) delete _value; - _value = other._value; + if (other._data) ++ *(u64*)other._count; + if (_data && !--*_count) { + delete _data; + delete _count; + } + _data = other._data; + _count = other._count; return *this; } const T& operator*() const { - return _value->data; + return *_data; } T& operator*() { - return _value->data; + return *_data; } const T* operator->() const { - return &_value->data; + return _data; } T* operator->() { - return &_value->data; + return _data; } operator bool() const { - return _value; + return _data; + } + + template + operator ref() { + ref copy; + if (_data) { + copy._data = (U*)_data; + copy._count = _count; + ++ *copy._count; + } + return copy; } }; -template -constexpr ref ref::null() { - return ref(NULL_VALUE); +template +ref newref(Args... args) { + return ref(new T(args...)); } #endif \ No newline at end of file diff --git a/util/slice.h b/util/slice.h index eb8943d..4921810 100644 --- a/util/slice.h +++ b/util/slice.h @@ -8,6 +8,8 @@ struct pair { T first; U second; + pair() {} + pair(const T& _first, const U& _second): first(_first), second(_second) {} diff --git a/util/str.cpp b/util/str.cpp index eb33f2e..6447c17 100644 --- a/util/str.cpp +++ b/util/str.cpp @@ -1,7 +1,6 @@ #include "str.h" #include "slice.h" #include "io.h" -#include void string::free() { if (_capacity > 16) delete[] data; diff --git a/util/utils.cpp b/util/utils.cpp new file mode 100644 index 0000000..092bdea --- /dev/null +++ b/util/utils.cpp @@ -0,0 +1,14 @@ +#include "utils.h" +#include "stdlib.h" + +void* operator new[](unsigned long n) { + return malloc(n); +} + +void* operator new(unsigned long n, void* p) { + return p; +} + +void operator delete[](void* ptr) { + free(ptr); +} \ No newline at end of file diff --git a/util/utils.h b/util/utils.h new file mode 100644 index 0000000..34283c5 --- /dev/null +++ b/util/utils.h @@ -0,0 +1,10 @@ +#ifndef BASIL_UTILS_H +#define BASIL_UTILS_H + +#include "defs.h" + +void* operator new[](unsigned long n); +void* operator new(unsigned long n, void* p); +void operator delete[](void* ptr); + +#endif \ No newline at end of file diff --git a/util/vec.h b/util/vec.h index e9a15d9..ae75735 100644 --- a/util/vec.h +++ b/util/vec.h @@ -3,7 +3,6 @@ #include "defs.h" #include "slice.h" -#include template class vector { @@ -144,4 +143,22 @@ class vector { } }; +template +void fill_vector(vector& v, const T& t) { + v.push(t); +} + +template +void fill_vector(vector& v, const T& t, const Args&... args) { + v.push(t); + fill_vector(v, args...); +} + +template +vector vector_of(const Args&... args) { + vector v; + fill_vector(v, args...); + return v; +} + #endif \ No newline at end of file From f092326af56696c193f082c9e4487d3f8781e249 Mon Sep 17 00:00:00 2001 From: elucent <9532786+elucent@users.noreply.github.com> Date: Tue, 12 Jan 2021 04:37:51 -0500 Subject: [PATCH 07/17] Added overloading. --- .gitignore | 2 +- Makefile | 5 +- compiler/builtin.cpp | 159 +++ compiler/builtin.h | 38 + compiler/eval.cpp | 2542 ++++++++++++++++++----------------- compiler/main.cpp | 64 +- compiler/type.cpp | 138 +- compiler/type.h | 62 +- compiler/values.cpp | 3028 ++++++++++++++++++++++-------------------- compiler/values.h | 49 +- util/hash.h | 46 + util/vec.h | 5 + 12 files changed, 3336 insertions(+), 2802 deletions(-) create mode 100644 compiler/builtin.cpp create mode 100644 compiler/builtin.h diff --git a/.gitignore b/.gitignore index dcb3500..a71774d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ .vscode/ -TODO *.o +TODO basil \ No newline at end of file diff --git a/Makefile b/Makefile index fda33ee..a624a0b 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,8 @@ OBJS := $(patsubst %.cpp,%.o,$(SRCS)) CXX := clang++ CXXHEADERS := -I. -Iutil -Ijasmine -Icompiler -CXXFLAGS := $(CXXHEADERS) -std=c++17 -ffast-math -fno-rtti -fno-exceptions -Wno-null-dereference -Wl,--unresolved-symbols=ignore-in-object-files +CXXFLAGS := $(CXXHEADERS) -std=c++17 -ffast-math -fno-rtti -fno-exceptions -Wno-null-dereference +LDFLAGS := -Wl,--unresolved-symbols=ignore-in-object-files clean: rm -f $(OBJS) *.o.tmp basil @@ -16,7 +17,7 @@ basil: CXXFLAGS += -g3 release: CXXFLAGS += -Os basil: $(OBJS) - $(CXX) $(CXXFLAGS) $^ -o $@ + $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ release: $(OBJS) $(CXX) $(CXXFLAGS) $^ -o basil diff --git a/compiler/builtin.cpp b/compiler/builtin.cpp new file mode 100644 index 0000000..b5e6edf --- /dev/null +++ b/compiler/builtin.cpp @@ -0,0 +1,159 @@ +#include "builtin.h" +#include "type.h" +#include "values.h" +#include "ast.h" +#include "env.h" + +namespace basil { + Builtin::Builtin() {} + + Builtin::Builtin(const Type* type, Function eval, Function compile): + _type(type), _eval(eval), _compile(compile) {} + + const Type* Builtin::type() const { + return _type; + } + + Value Builtin::eval(ref env, const Value& args) const { + return _eval(env, args); + } + + Value Builtin::compile(ref env, const Value& args) const { + return _compile(env, args); + } + + #define ARG(n) args.get_product()[n] + + Builtin + ADD, SUB, MUL, DIV, REM, + AND, OR, XOR, NOT, + EQUALS, NOT_EQUALS, LESS, GREATER, LESS_EQUAL, GREATER_EQUAL, + IS_EMPTY, HEAD, TAIL, CONS, + DISPLAY, READ_LINE, READ_WORD, READ_INT, + LENGTH, AT, STRCAT, SUBSTR, + ANNOTATE, TYPEOF, LIST_TYPE, + ASSIGN, IF; + + static void init_builtins() { + ADD = { + find(find(INT, INT), INT), + [](ref env, const Value& args) -> Value { + return Value(ARG(0).get_int() + ARG(1).get_int()); + }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryMath(ARG(0).loc(), AST_ADD, + ARG(0).get_runtime(), ARG(1).get_runtime())); + } + }; + SUB = { + find(find(INT, INT), INT), + [](ref env, const Value& args) -> Value { + return Value(ARG(0).get_int() - ARG(1).get_int()); + }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryMath(ARG(0).loc(), AST_SUB, + ARG(0).get_runtime(), ARG(1).get_runtime())); + } + }; + MUL = { + find(find(INT, INT), INT), + [](ref env, const Value& args) -> Value { + return Value(ARG(0).get_int() * ARG(1).get_int()); + }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryMath(ARG(0).loc(), AST_MUL, + ARG(0).get_runtime(), ARG(1).get_runtime())); + } + }; + DIV = { + find(find(INT, INT), INT), + [](ref env, const Value& args) -> Value { + return Value(ARG(0).get_int() / ARG(1).get_int()); + }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryMath(ARG(0).loc(), AST_DIV, + ARG(0).get_runtime(), ARG(1).get_runtime())); + } + }; + REM = { + find(find(INT, INT), INT), + [](ref env, const Value& args) -> Value { + return Value(ARG(0).get_int() % ARG(1).get_int()); + }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryMath(ARG(0).loc(), AST_REM, + ARG(0).get_runtime(), ARG(1).get_runtime())); + } + }; + AND = { + find(find(BOOL, BOOL), BOOL), + [](ref env, const Value& args) -> Value { + return Value(ARG(0).get_bool() && ARG(1).get_bool(), BOOL); + }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryLogic(ARG(0).loc(), AST_AND, + ARG(0).get_runtime(), ARG(1).get_runtime())); + } + }; + OR = { + find(find(BOOL, BOOL), BOOL), + [](ref env, const Value& args) -> Value { + return Value(ARG(0).get_bool() || ARG(1).get_bool(), BOOL); + }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryLogic(ARG(0).loc(), AST_OR, + ARG(0).get_runtime(), ARG(1).get_runtime())); + } + }; + XOR = { + find(find(BOOL, BOOL), BOOL), + [](ref env, const Value& args) -> Value { + return Value(ARG(0).get_bool() ^ ARG(1).get_bool(), BOOL); + }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryLogic(ARG(0).loc(), AST_XOR, + ARG(0).get_runtime(), ARG(1).get_runtime())); + } + }; + NOT = { + find(find(BOOL), BOOL), + [](ref env, const Value& args) -> Value { + return Value(!ARG(0).get_bool(), BOOL); + }, + [](ref env, const Value& args) -> Value { + return Value(new ASTNot(ARG(0).loc(), ARG(0).get_runtime())); + } + }; + TYPEOF = { + find(find(ANY), TYPE), + [](ref env, const Value& args) -> Value { + return Value(ARG(0).type(), TYPE); + }, + nullptr + }; + LIST_TYPE = { + find(find(TYPE), TYPE), + [](ref env, const Value& args) -> Value { + return Value(find(ARG(0).get_type()), TYPE); + }, + nullptr + }; + } + + static bool inited = false; + + void define_builtins(ref env) { + if (!inited) inited = true, init_builtins(); + env->infix("+", Value(env, ADD), 2, 20); + env->infix("-", Value(env, SUB), 2, 20); + env->infix("*", Value(env, MUL), 2, 40); + env->infix("/", Value(env, DIV), 2, 40); + env->infix("%", Value(env, REM), 2, 40); + env->infix("and", Value(env, AND), 2, 5); + env->infix("or", Value(env, OR), 2, 5); + env->infix("xor", Value(env, XOR), 2, 5); + env->def("not", Value(env, NOT), 1); + env->def("typeof", Value(env, TYPEOF), 1); + env->infix("list", Value(env, LIST_TYPE), 1, 80); + } +} \ No newline at end of file diff --git a/compiler/builtin.h b/compiler/builtin.h new file mode 100644 index 0000000..e7efa6f --- /dev/null +++ b/compiler/builtin.h @@ -0,0 +1,38 @@ +#ifndef BASIL_BUILTIN_H +#define BASIL_BUILTIN_H + +#include "util/defs.h" +#include "util/rc.h" + +namespace basil { + class Type; + class Value; + class Env; + + class Builtin { + using Function = Value(*)(ref, const Value&); + const Type* _type; + Function _eval, _compile; + public: + Builtin(); + Builtin(const Type* type, Function eval, Function compile); + + const Type* type() const; + Value eval(ref env, const Value& args) const; + Value compile(ref env, const Value& args) const; + }; + + extern void define_builtins(ref env); + + extern Builtin + ADD, SUB, MUL, DIV, REM, + AND, OR, XOR, NOT, + EQUALS, NOT_EQUALS, LESS, GREATER, LESS_EQUAL, GREATER_EQUAL, + IS_EMPTY, HEAD, TAIL, CONS, + DISPLAY, READ_LINE, READ_WORD, READ_INT, + LENGTH, AT, STRCAT, SUBSTR, + ANNOTATE, TYPEOF, LIST_TYPE, + ASSIGN, IF; +} + +#endif \ No newline at end of file diff --git a/compiler/eval.cpp b/compiler/eval.cpp index fa5f627..4d6472a 100644 --- a/compiler/eval.cpp +++ b/compiler/eval.cpp @@ -1,243 +1,244 @@ #include "eval.h" -#include "source.h" #include "ast.h" +#include "builtin.h" #include "driver.h" +#include "source.h" namespace basil { - Value builtin_add(ref env, const Value& args) { - return add(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_sub(ref env, const Value& args) { - return sub(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_mul(ref env, const Value& args) { - return mul(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_div(ref env, const Value& args) { - return div(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_rem(ref env, const Value& args) { - return rem(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_and(ref env, const Value& args) { - return logical_and(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_or(ref env, const Value& args) { - return logical_or(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_xor(ref env, const Value& args) { - return logical_xor(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_not(ref env, const Value& args) { - return logical_not(args.get_product()[0]); - } - - Value builtin_equal(ref env, const Value& args) { - return equal(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_inequal(ref env, const Value& args) { - return inequal(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_less(ref env, const Value& args) { - return less(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_greater(ref env, const Value& args) { - return greater(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_less_equal(ref env, const Value& args) { - return less_equal(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_greater_equal(ref env, const Value& args) { - return greater_equal(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_is_empty(ref env, const Value& args) { - return is_empty(args.get_product()[0]); - } - - Value builtin_head(ref env, const Value& args) { - return head(args.get_product()[0]); - } - - Value builtin_tail(ref env, const Value& args) { - return tail(args.get_product()[0]); - } - - Value builtin_cons(ref env, const Value& args) { - return cons(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_display(ref env, const Value& args) { - return display(args.get_product()[0]); - } - - Value gen_assign(ref env, const Value& dest, const Value& src) { - return list_of(Value("#="), list_of(Value("quote"), dest), src); - } - - Value builtin_assign_macro(ref env, const Value& args) { - return gen_assign(env, args.get_product()[0], - args.get_product()[1]); - } - - Value builtin_assign(ref env, const Value& args) { - return assign(env, args.get_product()[0], args.get_product()[1]); - } - - Value builtin_read_line(ref env, const Value& args) { - return new ASTNativeCall(NO_LOCATION, "_read_line", STRING); - } - - Value builtin_read_word(ref env, const Value& args) { - return new ASTNativeCall(NO_LOCATION, "_read_word", STRING); - } - - Value builtin_read_int(ref env, const Value& args) { - return new ASTNativeCall(NO_LOCATION, "_read_int", INT); - } - - Value builtin_length(ref env, const Value& args) { - return length(args.get_product()[0]); - } - - Value builtin_at(ref env, const Value& args) { - return at(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_strcat(ref env, const Value& args) { - return strcat(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_substr(ref env, const Value& args) { - return substr(args.get_product()[0], args.get_product()[1], args.get_product()[2]); - } - - Value builtin_if_macro(ref env, const Value& args) { - return list_of(Value("#?"), args.get_product()[0], - list_of(Value("quote"), args.get_product()[1]), - list_of(Value("quote"), args.get_product()[2])); - } - - Value builtin_if(ref env, const Value& args) { - Value cond = args.get_product()[0]; - - if (cond.is_runtime()) { - Value left = eval(env, args.get_product()[1]), - right = eval(env, args.get_product()[2]); - if (left.is_error() || right.is_error()) return error(); - if (!left.is_runtime()) left = lower(left); - if (!right.is_runtime()) right = lower(right); - ASTNode* ln = left.get_runtime(); - ASTNode* rn = right.get_runtime(); - return new ASTIf(cond.loc(), cond.get_runtime(), ln, rn); - } - - if (!cond.is_bool()) { - err(cond.loc(), "Expected boolean condition in if ", - "expression, given '", cond.type(), "'."); - return error(); - } - - if (cond.get_bool()) return eval(env, args.get_product()[1]); - else return eval(env, args.get_product()[2]); - } - - Value builtin_annotate(ref env, const Value& args) { - return annotate(args.get_product()[0], args.get_product()[1]); - } - - Value builtin_typeof(ref env, const Value& args) { - return type_of(args.get_product()[0]); - } - - ref create_root_env() { - ref root = newref(); - root->def("nil", Value(VOID)); - root->infix("+", new FunctionValue(root, builtin_add, 2), 2, 20); - root->infix("-", new FunctionValue(root, builtin_sub, 2), 2, 20); - root->infix("*", new FunctionValue(root, builtin_mul, 2), 2, 40); - root->infix("/", new FunctionValue(root, builtin_div, 2), 2, 40); - root->infix("%", new FunctionValue(root, builtin_rem, 2), 2, 40); - root->infix("and", new FunctionValue(root, builtin_and, 2), 2, 5); - root->infix("or", new FunctionValue(root, builtin_or, 2), 2, 5); - root->infix("xor", new FunctionValue(root, builtin_xor, 2), 2, 5); - root->def("not", new FunctionValue(root, builtin_not, 1), 1); - root->infix("==", new FunctionValue(root, builtin_equal, 2), 2, 10); - root->infix("!=", new FunctionValue(root, builtin_inequal, 2), 2, 10); - root->infix("<", new FunctionValue(root, builtin_less, 2), 2, 10); - root->infix(">", new FunctionValue(root, builtin_greater, 2), 2, 10); - root->infix("<=", new FunctionValue(root, builtin_less_equal, 2), 2, 10); - root->infix(">=", new FunctionValue(root, builtin_greater_equal, 2), 2, 10); - root->infix("empty?", new FunctionValue(root, builtin_is_empty, 1), 1, 60); - root->infix("head", new FunctionValue(root, builtin_head, 1), 1, 80); - root->infix("tail", new FunctionValue(root, builtin_tail, 1), 1, 80); - root->infix("::", new FunctionValue(root, builtin_cons, 2), 2, 15); - root->infix("cons", new FunctionValue(root, builtin_cons, 2), 2, 15); - root->def("display", new FunctionValue(root, builtin_display, 1), 1); - root->infix_macro("=", new MacroValue(root, builtin_assign_macro, 2), 2, 0); - root->infix("#=", new FunctionValue(root, builtin_assign, 2), 2, 0); - root->infix_macro("?", new MacroValue(root, builtin_if_macro, 3), 3, 2); - root->infix("#?", new FunctionValue(root, builtin_if, 3), 3, 2); - root->def("read-line", new FunctionValue(root, builtin_read_line, 0), 0); - root->def("read-word", new FunctionValue(root, builtin_read_word, 0), 0); - root->def("read-int", new FunctionValue(root, builtin_read_int, 0), 0); - root->infix("length", new FunctionValue(root, builtin_length, 1), 1, 50); - root->infix("at", new FunctionValue(root, builtin_at, 2), 2, 90); - root->infix("^", new FunctionValue(root, builtin_strcat, 2), 2, 20); - root->infix("substr", new FunctionValue(root, builtin_substr, 3), 3, 90); - root->def("annotate", new FunctionValue(root, builtin_annotate, 2), 2); - root->def("typeof", new FunctionValue(root, builtin_typeof, 1), 1); - - root->def("true", Value(true, BOOL)); - root->def("false", Value(false, BOOL)); - root->def("extern", Value(new ASTExtern({}))); - root->def("int", Value(INT, TYPE)); - root->def("symbol", Value(SYMBOL, TYPE)); - root->def("string", Value(STRING, TYPE)); - return root; - } - - // stubs - - Value eval_list(ref env, const Value& list); - Value eval(ref env, Value term); - Value infix(ref env, const Value& term, bool is_macro); - Value define(ref env, const Value& term, bool is_macro); - - // utilities - - vector to_ptr_vector(const Value& list) { - vector values; - const Value* v = &list; - while (v->is_list()) { - values.push(&v->get_list().head()); - v = &v->get_list().tail(); - } - return values; - } - - vector to_ptr_vector(Value& list) { - vector values; - Value* v = &list; - while (v->is_list()) { - values.push(&v->get_list().head()); - v = &v->get_list().tail(); - } - return values; - } + Value builtin_add(ref env, const Value& args) { + return add(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_sub(ref env, const Value& args) { + return sub(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_mul(ref env, const Value& args) { + return mul(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_div(ref env, const Value& args) { + return div(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_rem(ref env, const Value& args) { + return rem(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_and(ref env, const Value& args) { + return logical_and(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_or(ref env, const Value& args) { + return logical_or(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_xor(ref env, const Value& args) { + return logical_xor(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_not(ref env, const Value& args) { + return logical_not(args.get_product()[0]); + } + + Value builtin_equal(ref env, const Value& args) { + return equal(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_inequal(ref env, const Value& args) { + return inequal(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_less(ref env, const Value& args) { + return less(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_greater(ref env, const Value& args) { + return greater(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_less_equal(ref env, const Value& args) { + return less_equal(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_greater_equal(ref env, const Value& args) { + return greater_equal(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_is_empty(ref env, const Value& args) { + return is_empty(args.get_product()[0]); + } + + Value builtin_head(ref env, const Value& args) { + return head(args.get_product()[0]); + } + + Value builtin_tail(ref env, const Value& args) { + return tail(args.get_product()[0]); + } + + Value builtin_cons(ref env, const Value& args) { + return cons(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_display(ref env, const Value& args) { + return display(args.get_product()[0]); + } + + Value gen_assign(ref env, const Value& dest, const Value& src) { + return list_of(Value("#="), list_of(Value("quote"), dest), src); + } + + Value builtin_assign_macro(ref env, const Value& args) { + return gen_assign(env, args.get_product()[0], args.get_product()[1]); + } + + Value builtin_assign(ref env, const Value& args) { + return assign(env, args.get_product()[0], args.get_product()[1]); + } + + Value builtin_read_line(ref env, const Value& args) { + return new ASTNativeCall(NO_LOCATION, "_read_line", STRING); + } + + Value builtin_read_word(ref env, const Value& args) { + return new ASTNativeCall(NO_LOCATION, "_read_word", STRING); + } + + Value builtin_read_int(ref env, const Value& args) { + return new ASTNativeCall(NO_LOCATION, "_read_int", INT); + } + + Value builtin_length(ref env, const Value& args) { + return length(args.get_product()[0]); + } + + Value builtin_at(ref env, const Value& args) { + return at(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_strcat(ref env, const Value& args) { + return strcat(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_substr(ref env, const Value& args) { + return substr(args.get_product()[0], args.get_product()[1], args.get_product()[2]); + } + + Value builtin_if_macro(ref env, const Value& args) { + return list_of(Value("#?"), args.get_product()[0], list_of(Value("quote"), args.get_product()[1]), + list_of(Value("quote"), args.get_product()[2])); + } + + Value builtin_if(ref env, const Value& args) { + Value cond = args.get_product()[0]; + + if (cond.is_runtime()) { + Value left = eval(env, args.get_product()[1]), right = eval(env, args.get_product()[2]); + if (left.is_error() || right.is_error()) return error(); + if (!left.is_runtime()) left = lower(left); + if (!right.is_runtime()) right = lower(right); + ASTNode* ln = left.get_runtime(); + ASTNode* rn = right.get_runtime(); + return new ASTIf(cond.loc(), cond.get_runtime(), ln, rn); + } + + if (!cond.is_bool()) { + err(cond.loc(), "Expected boolean condition in if ", "expression, given '", cond.type(), "'."); + return error(); + } + + if (cond.get_bool()) return eval(env, args.get_product()[1]); + else + return eval(env, args.get_product()[2]); + } + + Value builtin_annotate(ref env, const Value& args) { + return annotate(args.get_product()[0], args.get_product()[1]); + } + + Value builtin_typeof(ref env, const Value& args) { + return type_of(args.get_product()[0]); + } + + ref create_root_env() { + ref root = newref(); + root->def("nil", Value(VOID)); + // root->infix("+", new FunctionValue(root, builtin_add, 2), 2, 20); + // root->infix("-", new FunctionValue(root, builtin_sub, 2), 2, 20); + // root->infix("*", new FunctionValue(root, builtin_mul, 2), 2, 40); + // root->infix("/", new FunctionValue(root, builtin_div, 2), 2, 40); + // root->infix("%", new FunctionValue(root, builtin_rem, 2), 2, 40); + // root->infix("and", new FunctionValue(root, builtin_and, 2), 2, 5); + // root->infix("or", new FunctionValue(root, builtin_or, 2), 2, 5); + // root->infix("xor", new FunctionValue(root, builtin_xor, 2), 2, 5); + // root->def("not", new FunctionValue(root, builtin_not, 1), 1); + // root->infix("==", new FunctionValue(root, builtin_equal, 2), 2, 10); + // root->infix("!=", new FunctionValue(root, builtin_inequal, 2), 2, 10); + // root->infix("<", new FunctionValue(root, builtin_less, 2), 2, 10); + // root->infix(">", new FunctionValue(root, builtin_greater, 2), 2, 10); + // root->infix("<=", new FunctionValue(root, builtin_less_equal, 2), 2, 10); + // root->infix(">=", new FunctionValue(root, builtin_greater_equal, 2), 2, 10); + // root->infix("empty?", new FunctionValue(root, builtin_is_empty, 1), 1, 60); + // root->infix("head", new FunctionValue(root, builtin_head, 1), 1, 80); + // root->infix("tail", new FunctionValue(root, builtin_tail, 1), 1, 80); + // root->infix("::", new FunctionValue(root, builtin_cons, 2), 2, 15); + // root->infix("cons", new FunctionValue(root, builtin_cons, 2), 2, 15); + // root->def("display", new FunctionValue(root, builtin_display, 1), 1); + // root->infix_macro("=", new MacroValue(root, builtin_assign_macro, 2), 2, 0); + // root->infix("#=", new FunctionValue(root, builtin_assign, 2), 2, 0); + // root->infix_macro("?", new MacroValue(root, builtin_if_macro, 3), 3, 2); + // root->infix("#?", new FunctionValue(root, builtin_if, 3), 3, 2); + // root->def("read-line", new FunctionValue(root, builtin_read_line, 0), 0); + // root->def("read-word", new FunctionValue(root, builtin_read_word, 0), 0); + // root->def("read-int", new FunctionValue(root, builtin_read_int, 0), 0); + // root->infix("length", new FunctionValue(root, builtin_length, 1), 1, 50); + // root->infix("at", new FunctionValue(root, builtin_at, 2), 2, 90); + // root->infix("^", new FunctionValue(root, builtin_strcat, 2), 2, 20); + // root->infix("substr", new FunctionValue(root, builtin_substr, 3), 3, 90); + // root->def("annotate", new FunctionValue(root, builtin_annotate, 2), 2); + // root->def("typeof", new FunctionValue(root, builtin_typeof, 1), 1); + define_builtins(root); + + root->def("true", Value(true, BOOL)); + root->def("false", Value(false, BOOL)); + root->def("extern", Value(new ASTExtern({}))); + root->def("int", Value(INT, TYPE)); + root->def("symbol", Value(SYMBOL, TYPE)); + root->def("string", Value(STRING, TYPE)); + root->def("type", Value(TYPE, TYPE)); + root->def("bool", Value(BOOL, TYPE)); + return root; + } + + // stubs + + Value eval_list(ref env, const Value& list); + Value eval(ref env, Value term); + Value infix(ref env, const Value& term, bool is_macro); + Value define(ref env, const Value& term, bool is_macro); + + // utilities + + vector to_ptr_vector(const Value& list) { + vector values; + const Value* v = &list; + while (v->is_list()) { + values.push(&v->get_list().head()); + v = &v->get_list().tail(); + } + return values; + } + + vector to_ptr_vector(Value& list) { + vector values; + Value* v = &list; + while (v->is_list()) { + values.push(&v->get_list().head()); + v = &v->get_list().tail(); + } + return values; + } bool introduces_env(const Value& list) { if (!list.is_list()) return false; @@ -245,1036 +246,1073 @@ namespace basil { if (!h.is_symbol()) return false; const string& name = symbol_for(h.get_symbol()); if (name == "def") { - return tail(list).is_list() && - head(tail(list)).is_list(); // is procedure - } - else if (name == "lambda" || name == "infix" - || name == "infix-macro" || name == "macro") return true; + return tail(list).is_list() && head(tail(list)).is_list(); // is procedure + } else if (name == "lambda" || name == "infix" || name == "infix-macro" || name == "macro") + return true; return false; } - static i64 traverse_deep = 0; - - void enable_deep() { - traverse_deep ++; - } - - void disable_deep() { - traverse_deep --; - if (traverse_deep < 0) traverse_deep = 0; - } - - void traverse_list(ref env, const Value& list, - void(*fn)(ref, const Value&)) { - vector vals = to_ptr_vector(list); - if (!introduces_env(list) || traverse_deep) - for (u32 i = 0; i < vals.size(); i ++) - fn(env, *vals[i]); - } - - void traverse_list(ref env, Value& list, - void(*fn)(ref, Value&)) { - vector vals = to_ptr_vector(list); - if (!introduces_env(list) || traverse_deep) - for (u32 i = 0; i < vals.size(); i ++) - fn(env, *vals[i]); - } - - void handle_splice(ref env, Value& item) { - if (!item.is_list()) return; - Value h = head(item); - if (h.is_symbol() && h.get_symbol() == symbol_value("splice")) { - Value t = tail(item); - if (t.is_void()) item = Value(VOID); - else { - Value t = tail(item); - prep(env, t); - item = eval(env, t); - } - } - else traverse_list(env, item, handle_splice); - } - - Value use(ref env, const Value& term); - - void handle_use(ref env, Value& item) { - if (!item.is_list()) return; - Value h = head(item); - if (h.is_symbol() && h.get_symbol() == symbol_value("use")) { - use(env, item); - item = list_of(string("list-of")); - } - else traverse_list(env, item, handle_use); - } - - void visit_macro_defs(ref env, const Value& item) { - if (!item.is_list()) return; - Value h = head(item); - if (h.is_symbol() && - (h.get_symbol() == symbol_value("macro") - || h.get_symbol() == symbol_value("infix-macro"))) { - bool infix = h.get_symbol() == symbol_value("infix-macro"); - u8 precedence = 0; - vector values = to_vector(item); - u32 i = 1; - if (values.size() >= 2 && infix && values[i].is_int()) { - precedence = u8(values[i].get_int()); - i ++; - } - if (i + 1 >= values.size() || - (!values[i].is_symbol() && - !(values[i].is_list() && head(values[i]).is_symbol()) && - !(values[i].is_list() && tail(values[i]).is_list() && - head(tail(values[i])).is_symbol()))) { - err(item.loc(), "Expected variable or function name ", - "in definition."); - return; - } - if (values.size() < 3) - err(item.loc(), "Expected value in definition."); - if (values[i].is_list()) { // procedure - if (infix) { - Value rest = tail(values[i]); - if (!rest.is_list()) { - err(rest.loc(), "Infix function must take at least one ", - "argument."); - return; - } - basil::infix(env, item, true); - } - else define(env, item, true); - } - else define(env, item, true); - } - else traverse_list(env, item, visit_macro_defs); - } - - bool symbol_matches(const Value& term, const string& sym) { - return term.is_symbol() && term.get_symbol() == symbol_value(sym); - } - - bool is_annotation(const Value& term) { - return term.is_list() && symbol_matches(head(term), "annotate"); - } - - Value annotation_type(const Value& term) { - return head(tail(tail(term))); - } - - bool is_valid_argument(const Value& term) { - return term.is_symbol() // name - || is_annotation(term) && tail(term).is_list() && head(tail(term)).is_symbol(); - } - - bool is_valid_def(const Value& term) { - return is_valid_argument(term) - || term.is_list() && head(term).is_symbol() - || is_annotation(term) && tail(term).is_list() && is_valid_def(head(tail(term))); - } - - bool is_valid_infix_def(const Value& term) { - return term.is_list() && tail(term).is_list() && head(tail(term)).is_symbol() - || is_annotation(term) && tail(term).is_list() && is_valid_infix_def(head(tail(term))); - } - - string get_arg_name(const Value& term) { - return is_annotation(term) ? get_arg_name(head(tail(term))) - : symbol_for(term.get_symbol()); - } - - string get_def_name(const Value& term) { - if (term.is_symbol()) return symbol_for(term.get_symbol()); - else if (is_annotation(term)) return get_def_name(head(tail(term))); - else return symbol_for(head(term).get_symbol()); - } - - Value get_def_info(const Value& term) { - if (is_annotation(term)) return get_def_info(head(tail(term))); - return term; - } - - string get_infix_name(const Value& term) { - if (is_annotation(term)) return get_infix_name(head(tail(term))); - return symbol_for(head(tail(term)).get_symbol()); - } - - void visit_defs(ref env, const Value& item) { - if (!item.is_list()) return; - Value h = head(item); - if (symbol_matches(h, "def") || symbol_matches(h, "infix")) { - bool infix = symbol_matches(h, "infix"); - u8 precedence = 0; - vector values = to_vector(item); - u32 i = 1; - if (values.size() >= 2 && infix && values[i].is_int()) { - precedence = u8(values[i].get_int()); // consume precedence - i ++; - } - if (i + 1 >= values.size() || - !(infix ? is_valid_infix_def(values[i]) : is_valid_def(values[i]))) { - err(item.loc(), "Expected variable or function name ", - "in definition."); - return; - } - if (values.size() < 3) - err(item.loc(), "Expected value in definition."); - if (is_valid_argument(values[i])) { // variable - string name = get_arg_name(values[i]); - // if (env->find(name)) { - // err(values[i].loc(), "Redefinition of '", name, "'."); - // return; - // } - env->def(name); - } - else { // procedure - if (infix) { - Value rest = tail(values[i]); - if (!rest.is_list()) { - err(rest.loc(), "Infix function must take at least one ", - "argument."); - return; - } - string name = get_infix_name(values[i]); - // if (env->find(name)) { - // err(values[i].loc(), "Redefinition of '", name, "'."); - // return; - // } - env->infix(name, to_vector(tail(rest)).size() + 1, - precedence); - } - else { - const string& name = get_def_name(values[i]); - // if (env->find(name)) { - // err(values[i].loc(), "Redefinition of '", name, "'."); - // return; - // } - env->def(name, to_vector(tail(values[i])).size()); - } - } - } - else traverse_list(env, item, visit_defs); - } - - void handle_macro(ref env, Value& item); - - Value expand(ref env, const Value& macro, const Value& arg) { - if (!macro.is_macro() && !macro.is_error()) { - err(macro.loc(), "Expanded value is not a macro."); - return error(); - } - if (!arg.is_product() && !arg.is_error()) { - err(arg.loc(), "Arguments not provided as a product."); - return error(); - } - if (macro.is_error() || arg.is_error()) return error(); - - const MacroValue& fn = macro.get_macro(); - if (fn.is_builtin()) { - return fn.get_builtin()(env, arg); - } - else { - ref env = fn.get_env(); - u32 argc = arg.get_product().size(), arity = fn.args().size(); - if (argc != arity) { - err(macro.loc(), "Procedure requires ", arity, " arguments, ", - argc, " provided."); - return error(); - } - for (u32 i = 0; i < arity; i ++) { - if (fn.args()[i] & KEYWORD_ARG_BIT) { - // keyword arg - Value argument = eval(env, arg.get_product()[i]); - if (!argument.is_symbol() || - argument.get_symbol() != - (fn.args()[i] & ARG_NAME_MASK)) { - err(arg.get_product()[i].loc(), "Expected keyword '", - symbol_for(fn.args()[i] & ARG_NAME_MASK), "', got '", - argument, "'."); - return error(); - } - } - else { - const string& argname = symbol_for(fn.args()[i] & ARG_NAME_MASK); - env->find(argname)->value = arg.get_product()[i]; - } - } - Value result = fn.body().clone(); - enable_deep(); - handle_splice(env, result); - disable_deep(); - prep(env, result); - return result; - } - } - - void handle_macro(ref env, Value& item) { - if (item.is_symbol()) { - const Def* def = env->find(symbol_for(item.get_symbol())); - if (def && def->is_macro_variable()) { - item = def->value.get_alias().value(); - return; - } - } - else if (item.is_list()) { - const Value& h = head(item); - if (h.is_symbol()) { - const Def* def = env->find(symbol_for(h.get_symbol())); - if (def && def->is_macro_procedure()) { - item = eval_list(env, item); - return; - } - } - } - traverse_list(env, item, handle_macro); - } - - Value apply_op(const Value& op, const Value& lhs) { - return list_of(op, lhs); - } - - Value apply_op(const Value& op, const Value& lhs, const Value& rhs) { - return list_of(op, lhs, rhs); - } - - Value apply_op(const Value& op, const Value& lhs, const vector& internals, - const Value& rhs) { - Value l = list_of(rhs); - for (i64 i = i64(internals.size()) - 1; i >= 0; i --) { - l = cons(internals[i], l); - } - return cons(op, cons(lhs, l)); - } - - pair unary_helper(ref env, const Value& lhs, - const Value& term); - pair infix_helper(ref env, const Value& lhs, - const Value& op, const Def* def, const Value& rhs, const Value& term, - const vector& internals); - pair infix_helper(ref env, const Value& lhs, - const Value& op, const Def* def, const Value& term); - pair infix_transform(ref env, const Value& term); - - pair infix_helper(ref env, const Value& lhs, - const Value& op, const Def* def, const Value& rhs, const Value& term, - const vector& internals) { - Value iter = term; - - if (iter.is_void()) - return { apply_op(op, lhs, internals, rhs), iter }; - Value next_op = head(iter); - if (!next_op.is_symbol()) - return { apply_op(op, lhs, internals, rhs), iter }; - const Def* next_def = env->find(symbol_for(next_op.get_symbol())); - if (!next_def || !next_def->is_infix) - return { apply_op(op, lhs, internals, rhs), iter }; - iter = tail(iter); // consume op - - if (next_def->precedence > def->precedence) { - if (next_def->arity == 1) { - return infix_helper(env, lhs, op, def, - apply_op(next_op, rhs), iter, internals); - } - auto p = infix_helper(env, rhs, next_op, next_def, iter); - return { apply_op(op, lhs, internals, p.first), p.second }; - } - else { - Value result = apply_op(op, lhs, rhs); - if (next_def->arity == 1) { - return unary_helper(env, apply_op(next_op, result), iter); - } - return infix_helper(env, apply_op(op, lhs, internals, rhs), - next_op, next_def, iter); - } - } - - pair infix_helper(ref env, const Value& lhs, - const Value& op, const Def* def, const Value& term) { - Value iter = term; - - vector internals; - if (def->arity > 2) for (u32 i = 0; i < def->arity - 2; i ++) { - auto p = infix_transform(env, iter); - internals.push(p.first); - iter = p.second; - } - - if (iter.is_void()) { - err(term.loc(), "Expected term in binary expression."); - return { error(), term }; - } - Value rhs = head(iter); - iter = tail(iter); // consume second term - - return infix_helper(env, lhs, op, def, rhs, iter, internals); - } - - pair unary_helper(ref env, const Value& lhs, - const Value& term) { - Value iter = term; - - if (iter.is_void()) return { lhs, iter }; - Value op = head(iter); - if (!op.is_symbol()) return { lhs, iter }; - const Def* def = env->find(symbol_for(op.get_symbol())); - if (!def || !def->is_infix) return { lhs, iter }; - iter = tail(iter); // consume op - - if (def->arity == 1) - return unary_helper(env, apply_op(op, lhs), iter); - return infix_helper(env, lhs, op, def, iter); - } - - pair infix_transform(ref env, const Value& term) { - Value iter = term; - - if (iter.is_void()) { - err(term.loc(), "Expected term in binary expression."); - return { error(), term }; - } - Value lhs = head(iter); // 1 + 2 -> 1 - iter = tail(iter); // consume first term - - if (iter.is_void()) return { lhs, iter }; - Value op = head(iter); - if (!op.is_symbol()) return { lhs, iter }; - const Def* def = env->find(symbol_for(op.get_symbol())); - if (!def || !def->is_infix) return { lhs, iter }; - iter = tail(iter); // consume op - - if (def->arity == 1) - return unary_helper(env, apply_op(op, lhs), iter); - return infix_helper(env, lhs, op, def, iter); - } - - Value handle_infix(ref env, const Value& term) { - vector infix_exprs; - Value iter = term; - while (iter.is_list()) { - auto p = infix_transform(env, iter); - infix_exprs.push(p.first); // next s-expr - iter = p.second; // move past it in source list - } - Value result = list_of(infix_exprs); - return result; - } - - void apply_infix(ref env, Value& item); - - void apply_infix_at(ref env, Value& item, u32 depth) { - Value* iter = &item; - while (depth && iter->is_list()) { - iter = &iter->get_list().tail(); - depth --; - } - *iter = handle_infix(env, *iter); - traverse_list(env, *iter, apply_infix); - } - - void apply_infix(ref env, Value& item) { - Value orig = item.clone(); - if (!item.is_list()) return; - Value h = head(item); - if (h.is_symbol()) { - const string& name = symbol_for(h.get_symbol()); - if (name == "macro" || name == "infix" - || name == "infix-macro" || name == "lambda" - || name == "quote" || name == "splice") return; - else if (name == "def") { - if (tail(item).is_list() && head(tail(item)).is_symbol()) - apply_infix_at(env, item, 2); - } - else if (name == "if" || name == "do" || name == "list-of") - apply_infix_at(env, item, 1); - else { - silence_errors(); - Value proc = eval(env, h); - unsilence_errors(); - if (proc.is_function()) apply_infix_at(env, item, 1); - else apply_infix_at(env, item, 0); - } - } - else apply_infix_at(env, item, 0); - } - - void prep(ref env, Value& term) { - handle_splice(env, term); - handle_use(env, term); - visit_macro_defs(env, term); - handle_macro(env, term); - visit_defs(env, term); - apply_infix(env, term); - handle_macro(env, term); - visit_defs(env, term); - } - - bool is_keyword(const Value& term) { - return term.is_list() && tail(term).is_list() && tail(tail(term)).is_void() - && symbol_matches(head(term), "quote") && symbol_matches(head(tail(term)), "quote"); - } - - // definition stuff - Value define(ref env, const Value& term, bool is_macro) { - vector values = to_vector(term); - - // visit_defs already does some error-checking, so we - // don't need to check the number of values or their types - // exhaustively. - - if (is_valid_argument(values[1])) { // variable - string name = get_arg_name(values[1]); - - if (is_macro) { - if (is_annotation(values[1])) { - err(values[1].loc(), "Type annotations are forbidden in macro definitions."); - return error(); + static i64 traverse_deep = 0; + + void enable_deep() { + traverse_deep++; + } + + void disable_deep() { + traverse_deep--; + if (traverse_deep < 0) traverse_deep = 0; + } + + void traverse_list(ref env, const Value& list, void (*fn)(ref, const Value&)) { + vector vals = to_ptr_vector(list); + if (!introduces_env(list) || traverse_deep) + for (u32 i = 0; i < vals.size(); i++) fn(env, *vals[i]); + } + + void traverse_list(ref env, Value& list, void (*fn)(ref, Value&)) { + vector vals = to_ptr_vector(list); + if (!introduces_env(list) || traverse_deep) + for (u32 i = 0; i < vals.size(); i++) fn(env, *vals[i]); + } + + void handle_splice(ref env, Value& item) { + if (!item.is_list()) return; + Value h = head(item); + if (h.is_symbol() && h.get_symbol() == symbol_value("splice")) { + Value t = tail(item); + if (t.is_void()) item = Value(VOID); + else { + Value t = tail(item); + prep(env, t); + item = eval(env, t); + } + } else + traverse_list(env, item, handle_splice); + } + + Value use(ref env, const Value& term); + + void handle_use(ref env, Value& item) { + if (!item.is_list()) return; + Value h = head(item); + if (h.is_symbol() && h.get_symbol() == symbol_value("use")) { + use(env, item); + item = list_of(string("list-of")); + } else + traverse_list(env, item, handle_use); + } + + void visit_macro_defs(ref env, const Value& item) { + if (!item.is_list()) return; + Value h = head(item); + if (h.is_symbol() && + (h.get_symbol() == symbol_value("macro") || h.get_symbol() == symbol_value("infix-macro"))) { + bool infix = h.get_symbol() == symbol_value("infix-macro"); + u8 precedence = 0; + vector values = to_vector(item); + u32 i = 1; + if (values.size() >= 2 && infix && values[i].is_int()) { + precedence = u8(values[i].get_int()); + i++; + } + if (i + 1 >= values.size() || + (!values[i].is_symbol() && !(values[i].is_list() && head(values[i]).is_symbol()) && + !(values[i].is_list() && tail(values[i]).is_list() && head(tail(values[i])).is_symbol()))) { + err(item.loc(), "Expected variable or function name ", "in definition."); + return; + } + if (values.size() < 3) err(item.loc(), "Expected value in definition."); + if (values[i].is_list()) { // procedure + if (infix) { + Value rest = tail(values[i]); + if (!rest.is_list()) { + err(rest.loc(), "Infix function must take at least one ", "argument."); + return; + } + basil::infix(env, item, true); + } else + define(env, item, true); + } else + define(env, item, true); + } else + traverse_list(env, item, visit_macro_defs); + } + + bool symbol_matches(const Value& term, const string& sym) { + return term.is_symbol() && term.get_symbol() == symbol_value(sym); + } + + bool is_annotation(const Value& term) { + return term.is_list() && symbol_matches(head(term), "annotate"); + } + + Value annotation_type(const Value& term) { + return head(tail(tail(term))); + } + + bool is_keyword(const Value& term) { + return term.is_list() && tail(term).is_list() && tail(tail(term)).is_void() && + symbol_matches(head(term), "quote") && head(tail(term)).is_symbol(); + } + + bool is_valid_argument(const Value& term) { + return term.is_symbol() // name + || is_annotation(term) && tail(term).is_list() && head(tail(term)).is_symbol(); + } + + bool is_valid_def(const Value& term) { + return is_valid_argument(term) || term.is_list() && head(term).is_symbol() || + is_annotation(term) && tail(term).is_list() && is_valid_def(head(tail(term))); + } + + bool is_valid_infix_def(const Value& term) { + return term.is_list() && tail(term).is_list() && head(tail(term)).is_symbol() || + is_annotation(term) && tail(term).is_list() && is_valid_infix_def(head(tail(term))); + } + + string get_arg_name(const Value& term) { + return is_annotation(term) ? get_arg_name(head(tail(term))) : symbol_for(term.get_symbol()); + } + + string get_def_name(const Value& term) { + if (term.is_symbol()) return symbol_for(term.get_symbol()); + else if (is_annotation(term)) + return get_def_name(head(tail(term))); + else + return symbol_for(head(term).get_symbol()); + } + + Value get_def_info(const Value& term) { + if (is_annotation(term)) return get_def_info(head(tail(term))); + return term; + } + + string get_infix_name(const Value& term) { + if (is_annotation(term)) return get_infix_name(head(tail(term))); + return symbol_for(head(tail(term)).get_symbol()); + } + + void visit_defs(ref env, const Value& item) { + if (!item.is_list()) return; + Value h = head(item); + if (symbol_matches(h, "def") || symbol_matches(h, "infix")) { + bool infix = symbol_matches(h, "infix"); + u8 precedence = 0; + vector values = to_vector(item); + u32 i = 1; + if (values.size() >= 2 && infix && values[i].is_int()) { + precedence = u8(values[i].get_int()); // consume precedence + i++; + } + if (i + 1 >= values.size() || !(infix ? is_valid_infix_def(values[i]) : is_valid_def(values[i]))) { + err(item.loc(), "Expected variable or function name ", "in definition."); + return; + } + if (values.size() < 3) err(item.loc(), "Expected value in definition."); + if (is_valid_argument(values[i])) { // variable + string name = get_arg_name(values[i]); + // if (env->find(name)) { + // err(values[i].loc(), "Redefinition of '", name, "'."); + // return; + // } + if (!env->find(name)) env->def(name); + } else { // procedure + if (infix) { + Value rest = tail(values[i]); + if (!rest.is_list()) { + err(rest.loc(), "Infix function must take at least one ", "argument."); + return; + } + string name = get_infix_name(values[i]); + // if (env->find(name)) { + // err(values[i].loc(), "Redefinition of '", name, "'."); + // return; + // } + if (!env->find(name)) env->infix(name, to_vector(tail(rest)).size() + 1, precedence); + } else { + const string& name = get_def_name(values[i]); + // if (env->find(name)) { + // err(values[i].loc(), "Redefinition of '", name, "'."); + // return; + // } + if (!env->find(name)) env->def(name, to_vector(tail(values[i])).size()); + } + } + } else + traverse_list(env, item, visit_defs); + } + + void handle_macro(ref env, Value& item); + + Value expand(ref env, const Value& macro, const Value& arg) { + if (!macro.is_macro() && !macro.is_error()) { + err(macro.loc(), "Expanded value is not a macro."); + return error(); + } + if (!arg.is_product() && !arg.is_error()) { + err(arg.loc(), "Arguments not provided as a product."); + return error(); + } + if (macro.is_error() || arg.is_error()) return error(); + + const MacroValue& fn = macro.get_macro(); + if (fn.is_builtin()) { + return error(); // fn.get_builtin()(env, arg); + } else { + ref env = fn.get_env(); + u32 argc = arg.get_product().size(), arity = fn.args().size(); + if (argc != arity) { + err(macro.loc(), "Procedure requires ", arity, " arguments, ", argc, " provided."); + return error(); + } + for (u32 i = 0; i < arity; i++) { + if (fn.args()[i] & KEYWORD_ARG_BIT) { + // keyword arg + Value argument = eval(env, arg.get_product()[i]); + if (!argument.is_symbol() || argument.get_symbol() != (fn.args()[i] & ARG_NAME_MASK)) { + err(arg.get_product()[i].loc(), "Expected keyword '", symbol_for(fn.args()[i] & ARG_NAME_MASK), + "', got '", argument, "'."); + return error(); + } + } else { + const string& argname = symbol_for(fn.args()[i] & ARG_NAME_MASK); + env->find(argname)->value = arg.get_product()[i]; + } + } + Value result = fn.body().clone(); + enable_deep(); + handle_splice(env, result); + disable_deep(); + prep(env, result); + return result; + } + } + + void handle_macro(ref env, Value& item) { + if (item.is_symbol()) { + const Def* def = env->find(symbol_for(item.get_symbol())); + if (def && def->is_macro_variable()) { + item = def->value.get_alias().value(); + return; + } + } else if (item.is_list()) { + const Value& h = head(item); + if (h.is_symbol()) { + const Def* def = env->find(symbol_for(h.get_symbol())); + if (def && def->is_macro_procedure()) { + item = eval_list(env, item); + return; + } + } + } + traverse_list(env, item, handle_macro); + } + + Value apply_op(const Value& op, const Value& lhs) { + return list_of(op, lhs); + } + + Value apply_op(const Value& op, const Value& lhs, const Value& rhs) { + return list_of(op, lhs, rhs); + } + + Value apply_op(const Value& op, const Value& lhs, const vector& internals, const Value& rhs) { + Value l = list_of(rhs); + for (i64 i = i64(internals.size()) - 1; i >= 0; i--) { l = cons(internals[i], l); } + return cons(op, cons(lhs, l)); + } + + pair unary_helper(ref env, const Value& lhs, const Value& term); + pair infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& rhs, + const Value& term, const vector& internals); + pair infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& term); + pair infix_transform(ref env, const Value& term); + + pair infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& rhs, + const Value& term, const vector& internals) { + Value iter = term; + + if (iter.is_void()) return {apply_op(op, lhs, internals, rhs), iter}; + Value next_op = head(iter); + if (!next_op.is_symbol()) return {apply_op(op, lhs, internals, rhs), iter}; + const Def* next_def = env->find(symbol_for(next_op.get_symbol())); + if (!next_def || !next_def->is_infix) return {apply_op(op, lhs, internals, rhs), iter}; + iter = tail(iter); // consume op + + if (next_def->precedence > def->precedence) { + if (next_def->arity == 1) { + return infix_helper(env, lhs, op, def, apply_op(next_op, rhs), iter, internals); + } + auto p = infix_helper(env, rhs, next_op, next_def, iter); + return {apply_op(op, lhs, internals, p.first), p.second}; + } else { + Value result = apply_op(op, lhs, rhs); + if (next_def->arity == 1) { return unary_helper(env, apply_op(next_op, result), iter); } + return infix_helper(env, apply_op(op, lhs, internals, rhs), next_op, next_def, iter); + } + } + + pair infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, + const Value& term) { + Value iter = term; + + vector internals; + if (def->arity > 2) + for (u32 i = 0; i < def->arity - 2; i++) { + auto p = infix_transform(env, iter); + internals.push(p.first); + iter = p.second; + } + + if (iter.is_void()) { + err(term.loc(), "Expected term in binary expression."); + return {error(), term}; + } + Value rhs = head(iter); + iter = tail(iter); // consume second term + + return infix_helper(env, lhs, op, def, rhs, iter, internals); + } + + pair unary_helper(ref env, const Value& lhs, const Value& term) { + Value iter = term; + + if (iter.is_void()) return {lhs, iter}; + Value op = head(iter); + if (!op.is_symbol()) return {lhs, iter}; + const Def* def = env->find(symbol_for(op.get_symbol())); + if (!def || !def->is_infix) return {lhs, iter}; + iter = tail(iter); // consume op + + if (def->arity == 1) return unary_helper(env, apply_op(op, lhs), iter); + return infix_helper(env, lhs, op, def, iter); + } + + pair infix_transform(ref env, const Value& term) { + Value iter = term; + + if (iter.is_void()) { + err(term.loc(), "Expected term in binary expression."); + return {error(), term}; + } + Value lhs = head(iter); // 1 + 2 -> 1 + iter = tail(iter); // consume first term + + if (iter.is_void()) return {lhs, iter}; + Value op = head(iter); + if (!op.is_symbol()) return {lhs, iter}; + const Def* def = env->find(symbol_for(op.get_symbol())); + if (!def || !def->is_infix) return {lhs, iter}; + iter = tail(iter); // consume op + + if (def->arity == 1) return unary_helper(env, apply_op(op, lhs), iter); + return infix_helper(env, lhs, op, def, iter); + } + + Value handle_infix(ref env, const Value& term) { + vector infix_exprs; + Value iter = term; + while (iter.is_list()) { + auto p = infix_transform(env, iter); + infix_exprs.push(p.first); // next s-expr + iter = p.second; // move past it in source list + } + Value result = list_of(infix_exprs); + return result; + } + + void apply_infix(ref env, Value& item); + + void apply_infix_at(ref env, Value& item, u32 depth) { + Value* iter = &item; + while (depth && iter->is_list()) { + iter = &iter->get_list().tail(); + depth--; + } + *iter = handle_infix(env, *iter); + traverse_list(env, *iter, apply_infix); + } + + void apply_infix(ref env, Value& item) { + Value orig = item.clone(); + if (!item.is_list()) return; + Value h = head(item); + if (h.is_symbol()) { + const string& name = symbol_for(h.get_symbol()); + if (name == "macro" || name == "infix" || name == "infix-macro" || name == "lambda" || name == "quote" || + name == "splice") + return; + else if (name == "def") { + if (tail(item).is_list() && head(tail(item)).is_symbol()) apply_infix_at(env, item, 2); + } else if (name == "if" || name == "do" || name == "list-of") + apply_infix_at(env, item, 1); + else { + silence_errors(); + Value proc = eval(env, h); + unsilence_errors(); + if (proc.is_function()) apply_infix_at(env, item, 1); + else + apply_infix_at(env, item, 0); + } + } else + apply_infix_at(env, item, 0); + } + + void prep(ref env, Value& term) { + handle_splice(env, term); + handle_use(env, term); + visit_macro_defs(env, term); + handle_macro(env, term); + visit_defs(env, term); + apply_infix(env, term); + handle_macro(env, term); + visit_defs(env, term); + } + + Value overload(ref env, Def* existing, const Value& func, const string& name) { + const Type* argst = (const ProductType*)((const FunctionType*)func.type())->arg(); + if (existing->value.is_function()) { // create new intersect + const Type* existing_args = ((const FunctionType*)existing->value.type())->arg(); + if (existing_args == argst) { + err(func.loc(), "Duplicate implementation for function '", name, + "': overload already exists for arguments ", argst, "."); + // todo: mention original definition's location + return error(); + } + map values; + values.put(existing->value.type(), existing->value); + values.put(func.type(), func); + existing->value = + Value(new IntersectValue(values), find(existing->value.type(), func.type())); + return Value(VOID); + } else if (existing->value.is_intersect()) { // add to existing intersect + for (const auto& p : existing->value.get_intersect()) { + if (p.first->kind() == KIND_FUNCTION) { + const Type* existing_args = ((const FunctionType*)p.second.type())->arg(); + if (existing_args == argst) { + err(func.loc(), "Duplicate implementation for function '", name, + "': overload already exists for arguments ", argst, "."); + // todo: mention original definition's location + return error(); + } + } + } + map values = existing->value.get_intersect().values(); + values.put(func.type(), func); + existing->value = + Value(new IntersectValue(values), find(existing->value.type(), func.type())); + } else { + err(func.loc(), "Cannot redefine symbol '", name, "' of type '", existing->value.type(), + "' as function."); + return error(); + } + } + + // definition stuff + Value define(ref env, const Value& term, bool is_macro) { + vector values = to_vector(term); + + // visit_defs already does some error-checking, so we + // don't need to check the number of values or their types + // exhaustively. + + if (is_valid_argument(values[1])) { // variable + string name = get_arg_name(values[1]); + + if (is_macro) { + if (is_annotation(values[1])) { + err(values[1].loc(), "Type annotations are forbidden in macro definitions."); + return error(); + } + env->def_macro(name, Value(new AliasValue(values[2]))); + return Value(VOID); + } else { + Value init = eval(env, values[2]); + if (env->is_runtime()) init = lower(init); + if (is_annotation(values[1])) init = annotate(init, eval(env, annotation_type(values[1]))); + env->def(name, init); + if (init.is_runtime()) + return new ASTDefine(values[0].loc(), env, values[1].get_symbol(), init.get_runtime()); + return Value(VOID); + } + } else if (values[1].is_list()) { // procedure + string name = get_def_name(values[1]); + + ref function_env = newref(env); + vector args = to_vector(tail(get_def_info(values[1]))); + vector argnames; + vector body; + vector argts; + for (const Value& v : args) { + if (is_valid_argument(v)) { + string name = get_arg_name(v); + argnames.push(symbol_value(name)); + function_env->def(name); + } else if (is_keyword(v)) { + argnames.push(eval(env, v).get_symbol() | KEYWORD_ARG_BIT); + } else { + err(v.loc(), + "Only symbols, annotated symbols, and quoted symbols " + "are permitted within an argument list; given '", + v, "'."); + return error(); + } + if (is_annotation(v)) { + if (is_macro) { + err(v.loc(), "Type annotations are forbidden in macro definitions."); + return error(); + } + Value tval = eval(env, annotation_type(v)); + if (!tval.is_type()) { + if (tval.type()->coerces_to(TYPE)) tval = cast(tval, TYPE); + else { + err(v.loc(), "Expected type value in annotation, given '", tval.type(), "'."); + return error(); + } + } + // body.push(v); + // body.push(list_of(Value("list-of"))); + argts.push(tval.get_type()); + } else if (!is_keyword(v)) + argts.push(ANY); + } + for (u32 i = 2; i < values.size(); i++) body.push(values[i]); + Value body_term = cons(Value("do"), list_of(body)); + if (is_macro) { + if (is_annotation(values[1])) { + err(values[1].loc(), "Type annotations are forbidden in macro definitions."); + return error(); + } + Value mac(new MacroValue(function_env, argnames, body_term)); + env->def_macro(name, mac, argnames.size()); + } else { + const Type* returntype = ANY; + if (is_annotation(values[1])) { + Value tval = eval(env, annotation_type(values[1])); + if (!tval.is_type()) { + if (tval.type()->coerces_to(TYPE)) tval = cast(tval, TYPE); + else { + err(values[1].loc(), "Expected type value in annotation, given '", tval.type(), "'."); + return error(); + } + } + returntype = tval.get_type(); + } + const Type* argst = find(argts); + Value func(new FunctionValue(function_env, argnames, body_term, symbol_value(name)), + find(argst, returntype)); + Def* existing = env->find(name); + if (existing && !existing->value.is_void()) { + return overload(env, existing, func, name); + } else env->def(name, func, argnames.size()); + // if (argst->concrete()) instantiate(func.loc(), func.get_function(), find(argts)); + } + return Value(VOID); + } else { + err(values[1].loc(), "First parameter to definition must be ", + "a symbol (for variable or alias) or list (for procedure ", "or macro)."); + return error(); + } + } + + Value infix(ref env, const Value& term, bool is_macro) { + vector values = to_vector(term); + + u8 precedence = 0; + + u32 i = 1; + if (values[i].is_int()) { // precedence + precedence = (u8)values[i].get_int(); + i++; + } + + Value def_info = get_def_info(values[i]); + const string& name = symbol_for(head(tail(def_info)).get_symbol()); + + vector args; + args.push(head(def_info)); + Value v = tail(tail(def_info)); + while (v.is_list()) { + args.push(head(v)); + v = tail(v); + } + if (args.size() == 0) { + err(values[i].loc(), "Expected argument list in infix ", "definition."); + return error(); + } + i++; + ref function_env = newref(env); + vector argnames; + vector argts; + for (const Value& v : args) { + if (is_valid_argument(v)) { + string name = get_arg_name(v); + argnames.push(symbol_value(name)); + function_env->def(name); + } else if (is_keyword(v)) { + argnames.push(eval(env, v).get_symbol() | KEYWORD_ARG_BIT); + } else { + err(v.loc(), + "Only symbols, annotated symbols, and quoted symbols " + "are permitted within an argument list; given '", + v, "'."); + return error(); + } + if (is_annotation(v)) { + if (is_macro) { + err(v.loc(), "Type annotations are forbidden in macro definitions."); + return error(); + } + Value tval = eval(env, annotation_type(v)); + if (!tval.is_type()) { + if (tval.type()->coerces_to(TYPE)) tval = cast(tval, TYPE); + else { + err(v.loc(), "Expected type value in annotation, given '", tval.type(), "'."); + return error(); + } + } + // body.push(v); + // body.push(list_of(Value("list-of"))); + argts.push(tval.get_type()); + } else if (!is_keyword(v)) + argts.push(ANY); } - env->def_macro(name, Value(new AliasValue(values[2]))); - return Value(VOID); - } - else { - Value init = eval(env, values[2]); - if (env->is_runtime()) - init = lower(init); - if (is_annotation(values[1])) - init = annotate(init, eval(env, annotation_type(values[1]))); - env->def(name, init); - if (init.is_runtime()) - return new ASTDefine(values[0].loc(), env, - values[1].get_symbol(), init.get_runtime()); - return Value(VOID); - } - } - else if (values[1].is_list()) { // procedure - string name = get_def_name(values[1]); - - ref function_env = newref(env); - vector args = to_vector(tail(get_def_info(values[1]))); - vector argnames; - vector body; - vector argts; - for (const Value& v : args) { - if (is_valid_argument(v)) { - string name = get_arg_name(v); - argnames.push(symbol_value(name)); - function_env->def(name); + vector body; + for (int j = i; j < values.size(); j++) body.push(values[j]); + Value body_term = cons(Value("do"), list_of(body)); + if (is_macro) { + if (is_annotation(values[i])) { + err(values[i].loc(), "Type annotations are forbidden in macro definitions."); + return error(); + } + Value mac(new MacroValue(function_env, argnames, body_term)); + env->infix_macro(name, mac, argnames.size(), precedence); + } else { + const Type* returntype = ANY; + if (is_annotation(values[i])) { + Value tval = eval(env, annotation_type(values[i])); + if (!tval.is_type()) { + if (tval.type()->coerces_to(TYPE)) tval = cast(tval, TYPE); + else { + err(values[i].loc(), "Expected type value in annotation, given '", tval.type(), "'."); + return error(); + } + } + returntype = tval.get_type(); + } + const Type* argst = find(argts); + Value func(new FunctionValue(function_env, argnames, body_term, symbol_value(name)), + find(argst, returntype)); + Def* existing = env->find(name); + if (existing && !existing->value.is_void()) { + if (existing->precedence != precedence) { + err(func.loc(), "Cannot overload infix function '", name, "' with function of incompatible ", + "precedence ", (u32)precedence, " (original was ", (u32)existing->precedence, ")."); + return error(); + } + return overload(env, existing, func, name); + } else env->infix(name, func, argnames.size(), precedence); + // if (argst->concrete()) instantiate(func.loc(), func.get_function(), find(argts)); } - else if (is_keyword(v)) { - argnames.push(eval(env, v).get_symbol() | KEYWORD_ARG_BIT); - } - else { - err(v.loc(), "Only symbols, annotated symbols, and quoted symbols " - "are permitted within an argument list; given '", v, "'."); - return error(); - } - if (is_annotation(v)) { - if (is_macro) { - err(v.loc(), "Type annotations are forbidden in macro definitions."); + return Value(VOID); + } + + Value lambda(ref env, const Value& term) { + vector values = to_vector(term); + if (values.size() < 3) { + err(values[0].loc(), "Expected argument list and body in ", "lambda expression."); return error(); - } - Value tval = eval(env, annotation_type(v)); - if (!tval.is_type()) { - err(v.loc(), "Expected type value in annotation, given '", tval.type(), "'."); + } + if (!values[1].is_list()) { + err(values[1].loc(), "Expected argument list in lambda ", "expression."); return error(); - } - body.push(v); - body.push(list_of(Value("list-of"))); - argts.push(tval.get_type()); } - else argts.push(find()); - } - for (u32 i = 2; i < values.size(); i ++) body.push(values[i]); - Value body_term = cons(Value("do"), list_of(body)); - if (is_macro) { - if (is_annotation(values[1])) { - err(values[1].loc(), "Type annotations are forbidden in macro definitions."); - return error(); + ref function_env = newref(env); + vector args = to_vector(values[1]); + vector argnames; + vector argts; + for (const Value& v : args) { + if (is_valid_argument(v)) { + string name = get_arg_name(v); + argnames.push(symbol_value(name)); + function_env->def(name); + } else if (is_keyword(v)) { + argnames.push(eval(env, v).get_symbol() | KEYWORD_ARG_BIT); + } else { + err(v.loc(), + "Only symbols, annotated symbols, and quoted symbols " + "are permitted within an argument list; given '", + v, "'."); + return error(); + } + if (is_annotation(v)) { + Value tval = eval(env, annotation_type(v)); + if (!tval.is_type()) { + if (tval.type()->coerces_to(TYPE)) tval = cast(tval, TYPE); + else { + err(v.loc(), "Expected type value in annotation, given '", tval.type(), "'."); + return error(); + } + } + // body.push(v); + // body.push(list_of(Value("list-of"))); + argts.push(tval.get_type()); + } else if (!is_keyword(v)) + argts.push(ANY); } - Value mac(new MacroValue(function_env, argnames, body_term)); - env->def_macro(name, mac, argnames.size()); - } - else { - Value func(new FunctionValue(function_env, argnames, body_term, - symbol_value(name))); + const Type* returntype = ANY; if (is_annotation(values[1])) { - Value tval = eval(env, annotation_type(values[1])); - if (!tval.is_type()) { - err(values[1].loc(), "Expected type value in annotation, given '", tval.type(), "'."); + Value tval = eval(env, annotation_type(values[1])); + if (!tval.is_type()) { + if (tval.type()->coerces_to(TYPE)) tval = cast(tval, TYPE); + else { + err(values[1].loc(), "Expected type value in annotation, given '", tval.type(), "'."); + return error(); + } + } + returntype = tval.get_type(); + } + const Type* argst = find(argts); + vector body; + for (u32 i = 2; i < values.size(); i++) body.push(values[i]); + Value body_term = list_of(body); + prep(function_env, body_term); + return Value(new FunctionValue(function_env, argnames, cons(Value("do"), body_term)), + find(argst, returntype)); + } + + Value do_block(ref env, const Value& term) { + const Value& todo = tail(term); + + const Value* v = &todo; + vector values; + bool runtime = false; + while (v->is_list()) { + values.push(eval(env, v->get_list().head())); + if (values.back().is_runtime()) runtime = true; + v = &v->get_list().tail(); + } + if (values.size() == 0) return Value(VOID); + if (runtime) { + vector nodes; + for (Value& v : values) + if (!v.is_runtime()) { + v = lower(v); + if (v.is_error()) return v; + } + for (Value& v : values) nodes.push(v.get_runtime()); + return new ASTBlock(term.loc(), nodes); + } + return values.back(); + } + + u64 get_keyword(const Value& v) { + if (v.is_symbol()) return v.get_symbol(); + else if (v.is_list() && head(v).is_symbol() && head(v).get_symbol() == symbol_value("quote") && + tail(v).is_list() && head(tail(v)).is_symbol()) + return head(tail(v)).get_symbol(); + return 0; + } + + bool is_keyword(const Value& v, const string& word) { + if (v.is_symbol()) return v.get_symbol() == symbol_value(word); + else if (v.is_list() && head(v).is_symbol() && head(v).get_symbol() == symbol_value("quote") && + tail(v).is_list() && head(tail(v)).is_symbol()) + return head(tail(v)).get_symbol() == symbol_value(word); + return false; + } + + Value if_expr(ref env, const Value& term) { + Value params = tail(term); + prep(env, params); + Value cond, if_true, if_false; + bool has_else = false; + vector if_true_vals, if_false_vals; + if (!params.is_list()) { + err(term.loc(), "Expected condition in if expression."); return error(); - } - func = annotate(func, Value(find(find(argts), tval.get_type()), TYPE)); } - env->def(name, func, argnames.size()); - if (find(argts)->concrete()) - instantiate(func.loc(), func.get_function(), find(argts)); - } - return Value(VOID); - } - else { - err(values[1].loc(), "First parameter to definition must be ", - "a symbol (for variable or alias) or list (for procedure ", - "or macro)."); - return error(); - } - } - - Value infix(ref env, const Value& term, bool is_macro) { - vector values = to_vector(term); - - u8 precedence = 0; - - u32 i = 1; - if (values[i].is_int()) { // precedence - precedence = (u8)values[i].get_int(); - i ++; - } - if (i + 1 >= values.size()) { - err(values[0].loc(), "Expected argument list and body in ", - "infix definition."); - return error(); - } - if (!values[i].is_list()) { - err(values[i].loc(), "Expected name and argument list for ", - "infix definition."); - return error(); - } - if (!head(tail(values[i])).is_symbol()) { - err(tail(values[i]).loc(), "Expected name for infix definition."); - return error(); - } - const string& name = symbol_for(head(tail(values[i])).get_symbol()); - - vector args; - args.push(head(values[i])); - Value v = tail(tail(values[i])); - while (v.is_list()) { - args.push(head(v)); - v = tail(v); - } - if (args.size() == 0) { - err(values[i].loc(), "Expected argument list in infix ", - "definition."); - return error(); - } - i ++; - ref function_env = newref(env); - vector argnames; - for (const Value& v : args) { - if (v.is_symbol()) { - argnames.push(v.get_symbol()); - function_env->def(symbol_for(v.get_symbol())); - } - else if (v.is_list() && head(v).is_symbol() - && symbol_for(head(v).get_symbol()) == "quote" - && head(tail(v)).is_symbol()) { - argnames.push(eval(env, v).get_symbol() | KEYWORD_ARG_BIT); - } - else { - err(v.loc(), "Only symbols and quoted symbols are permitted ", - "within an argument list."); - return error(); - } - } - vector body; - for (; i < values.size(); i ++) body.push(values[i]); - Value body_term = cons(Value("do"), list_of(body)); - if (is_macro) { - Value mac(new MacroValue(function_env, argnames, body_term)); - env->infix_macro(name, mac, argnames.size(), precedence); - return Value(VOID); - } - else { - prep(function_env, body_term); - Value func(new FunctionValue(function_env, argnames, body_term, - symbol_value(name))); - env->infix(name, func, argnames.size(), precedence); - return Value(VOID); - } - } - - Value lambda(ref env, const Value& term) { - vector values = to_vector(term); - if (values.size() < 3) { - err(values[0].loc(), "Expected argument list and body in ", - "lambda expression."); - return error(); - } - if (!values[1].is_list()) { - err(values[1].loc(), "Expected argument list in lambda ", - "expression."); - return error(); - } - ref function_env = newref(env); - vector args = to_vector(values[1]); - vector argnames; - for (const Value& v : args) { - if (v.is_symbol()) { - argnames.push(v.get_symbol()); - function_env->def(symbol_for(v.get_symbol())); - } - else { - err(v.loc(), "Only symbols are permitted ", - "within a lambda's argument list."); - return error(); - } - } - vector body; - for (u32 i = 2; i < values.size(); i ++) body.push(values[i]); - Value body_term = list_of(body); - prep(function_env, body_term); - return Value(new FunctionValue(function_env, argnames, - cons(Value("do"), body_term))); - } - - Value do_block(ref env, const Value& term) { - const Value& todo = tail(term); - - const Value* v = &todo; - vector values; - bool runtime = false; - while (v->is_list()) { - values.push(eval(env, v->get_list().head())); - if (values.back().is_runtime()) runtime = true; - v = &v->get_list().tail(); - } - if (values.size() == 0) return Value(VOID); - if (runtime) { - vector nodes; - for (Value& v : values) if (!v.is_runtime()) { - v = lower(v); - if (v.is_error()) return v; - } - for (Value& v : values) nodes.push(v.get_runtime()); - return new ASTBlock(term.loc(), nodes); - } - return values.back(); - } - - u64 get_keyword(const Value& v) { - if (v.is_symbol()) return v.get_symbol(); - else if (v.is_list() && head(v).is_symbol() - && head(v).get_symbol() == symbol_value("quote") - && tail(v).is_list() && head(tail(v)).is_symbol()) - return head(tail(v)).get_symbol(); - return 0; - } - - bool is_keyword(const Value& v, const string& word) { - if (v.is_symbol()) return v.get_symbol() == symbol_value(word); - else if (v.is_list() && head(v).is_symbol() - && head(v).get_symbol() == symbol_value("quote") - && tail(v).is_list() && head(tail(v)).is_symbol()) - return head(tail(v)).get_symbol() == symbol_value(word); - return false; - } - - Value if_expr(ref env, const Value& term) { - Value params = tail(term); - prep(env, params); - Value cond, if_true, if_false; - bool has_else = false; - vector if_true_vals, if_false_vals; - if (!params.is_list()) { - err(term.loc(), "Expected condition in if expression."); - return error(); - } - cond = head(params); - params = tail(params); - while (params.is_list() && !(is_keyword(head(params), "else") - || is_keyword(head(params), "elif"))) { - if_true_vals.push(head(params)); - params = tail(params); - } - if_true = cons(Value("do"), list_of(if_true_vals)); - if (!params.is_list()) { - if_false = list_of(Value("list-of")); - } - else if (get_keyword(head(params)) == symbol_value("elif")) { - if_false = if_expr(env, params); - return list_of(Value("#?"), - cond, - list_of(Value("quote"), if_true), - list_of(Value("quote"), if_false)); - } - else { - params = tail(params); - while (params.is_list()) { - if_false_vals.push(head(params)); - params = tail(params); - } - if_false = cons(Value("do"), list_of(if_false_vals)); - } - return list_of(Value("#?"), - cond, - list_of(Value("quote"), if_true), - list_of(Value("quote"), if_false)); - } - - bool is_quoted_symbol(const Value& val) { - return val.is_list() && tail(val).is_list() - && head(val).is_symbol() && head(val).get_symbol() == symbol_value("quote") - && head(tail(val)).is_symbol(); - } - - void find_assigns(ref env, const Value& term, - set& dests) { - if (!term.is_list()) return; - Value h = head(term); - if (h.is_symbol() && h.get_symbol() == symbol_value("#=")) { - if (tail(term).is_list() && is_quoted_symbol(head(tail(term)))) { - u64 sym = eval(env, head(tail(term))).get_symbol(); - Def* def = env->find(symbol_for(sym)); - if (def) dests.insert(sym); - } - } - if (!introduces_env(term)) { - const Value* v = &term; - while (v->is_list()) { - find_assigns(env, v->get_list().head(), dests); - v = &v->get_list().tail(); - } - } - } - - Value while_stmt(ref env, const Value& term) { - vector values = to_vector(term); - if (values.size() < 3) { - err(term.loc(), "Incorrect number of arguments for while ", - "statement. Expected condition and body."); - return error(); - } - - Value body = cons(Value("do"), tail(tail(term))); - - set dests; - find_assigns(env, head(tail(term)), dests); - find_assigns(env, body, dests); - - vector nodes; - for (u64 u : dests) { - Def* def = env->find(symbol_for(u)); - if (!def->value.is_runtime()) { - Value new_value = lower(def->value); - nodes.push( - new ASTDefine(new_value.loc(), env, u, new_value.get_runtime())); - def->value = new_value; - } - } - - Value cond = eval(env, head(tail(term))); - if (cond.is_error()) return error(); - if (!cond.is_runtime()) cond = lower(cond); - - body = eval(env, body); - if (body.is_error()) return error(); - if (!body.is_runtime()) body = lower(body); - nodes.push(new ASTWhile(term.loc(), cond.get_runtime(), - body.get_runtime())); - return nodes.size() == 1 ? nodes[0] : new ASTBlock(term.loc(), nodes); - } - - Value list_of(ref env, const Value& term) { - Value items = tail(term); - - const Value* v = &items; - vector vals; - while (v->is_list()) { - vals.push(eval(env, v->get_list().head())); - v = &v->get_list().tail(); - } - return list_of(vals); - } - - Value tuple_of(ref env, const Value& term) { - Value items = tail(term); - - const Value* v = &items; - vector vals; - while (v->is_list()) { - vals.push(eval(env, v->get_list().head())); - v = &v->get_list().tail(); - } - return tuple_of(vals); - } - - Value array_of(ref env, const Value& term) { - Value items = tail(term); - - const Value* v = &items; - vector vals; - while (v->is_list()) { - vals.push(eval(env, v->get_list().head())); - v = &v->get_list().tail(); - } - return array_of(vals); - } - - struct CompilationUnit { - Source source; - ref env; - }; - - static map modules; - - Value use(ref env, const Value& term) { - if (!tail(term).is_list() || !head(tail(term)).is_symbol()) { - err(tail(term).loc(), "Expected symbol in use expression, given '", - tail(term), "'."); - return error(); - } - Value h = head(tail(term)); - string path = symbol_for(h.get_symbol()); - path += ".bl"; - - ref module; - auto it = modules.find(path); - if (it != modules.end()) { - module = it->second.env; - } - else { - CompilationUnit unit { Source((const char*)path.raw()), {} }; - if (!unit.source.begin().peek()) { - err(h.loc(), "Could not load source file at path '", path, "'."); - return error(); - } - module = unit.env = load(unit.source); - if (error_count()) return error(); - - for (const auto& p : *module) { - if (env->find(p.first)) { - err(term.loc(), "Module '", symbol_for(h.get_symbol()), - "' redefines '", p.first, "' in the current environment."); - return error(); - } - } - - modules.put(path, unit); - } - - env->import(module); - return Value(VOID); - } - - Value eval_list(ref env, const Value& term) { - Value h = head(term); - if (h.is_symbol()) { - const string& name = symbol_for(h.get_symbol()); - if (name == "quote") return head(tail(term)).clone(); - else if (name == "def") return define(env, term, false); - else if (name == "infix") return infix(env, term, false); - else if (name == "macro") return define(env, term, true); - else if (name == "infix-macro") return infix(env, term, true); - else if (name == "lambda") return lambda(env, term); - else if (name == "do") return do_block(env, term); - else if (name == "if") return eval(env, if_expr(env, term)); - else if (name == "list-of") return list_of(env, term); - else if (name == "tuple-of") return tuple_of(env, term); - else if (name == "array-of") return array_of(env, term); - else if (name == "use") return use(env, term); - else if (name == "while") return while_stmt(env, term); - } - - Value first = eval(env, h); - - if (h.is_symbol() && tail(term).is_void()) { - const Def* def = env->find(symbol_for(h.get_symbol())); - if (def->is_infix) return first; - } - - if (first.is_macro()) { - vector args; - const Value* v = &term.get_list().tail(); - u32 i = 0; - while (v->is_list()) { - if (i < first.get_macro().arity() - && first.get_macro().args()[i] & KEYWORD_ARG_BIT - && v->get_list().head().is_symbol()) - args.push(list_of(Value("quote"), v->get_list().head())); - else args.push(v->get_list().head()); - v = &v->get_list().tail(); - i ++; - } - if (args.size() != first.get_macro().arity()) { - err(term.loc(), "Macro procedure expects ", - first.get_macro().arity(), " arguments, ", - args.size(), " provided."); - return error(); - } - return expand(env, first, Value(new ProductValue(args))); - } - else if (first.is_function()) { - vector args; - Value args_term = tail(term); - const Value* v = &args_term; - u32 i = 0; - while (v->is_list()) { - if (i < first.get_function().arity() - && first.get_function().args()[i] & KEYWORD_ARG_BIT - && v->get_list().head().is_symbol()) { - args.push(v->get_list().head()); // leave keywords quoted - } - else args.push(eval(env, v->get_list().head())); - - v = &v->get_list().tail(); - i ++; - } - if (args.size() != first.get_function().arity()) { - err(term.loc(), "Procedure expects ", - first.get_function().arity(), " arguments, ", - args.size(), " provided."); - return error(); - } - return call(env, first, Value(new ProductValue(args))); - } - else if (first.is_runtime() && - first.get_runtime()->type()->kind() == KIND_FUNCTION) { - vector args; - Value args_term = tail(term); - const Value* v = &args_term; - u32 i = 0; - while (v->is_list()) { - args.push(eval(env, v->get_list().head())); - - v = &v->get_list().tail(); - i ++; - } - const FunctionType* fntype = (const FunctionType*)first.get_runtime()->type(); - const ProductType* args_type = (const ProductType*)fntype->arg(); - if (args.size() != args_type->count()) { - err(term.loc(), "Procedure expects ", - args_type->count(), " arguments, ", - args.size(), " provided."); + cond = head(params); + params = tail(params); + while (params.is_list() && !(is_keyword(head(params), "else") || is_keyword(head(params), "elif"))) { + if_true_vals.push(head(params)); + params = tail(params); + } + if_true = cons(Value("do"), list_of(if_true_vals)); + if (!params.is_list()) { + if_false = list_of(Value("list-of")); + } else if (get_keyword(head(params)) == symbol_value("elif")) { + if_false = if_expr(env, params); + return list_of(Value("#?"), cond, list_of(Value("quote"), if_true), list_of(Value("quote"), if_false)); + } else { + params = tail(params); + while (params.is_list()) { + if_false_vals.push(head(params)); + params = tail(params); + } + if_false = cons(Value("do"), list_of(if_false_vals)); + } + return list_of(Value("#?"), cond, list_of(Value("quote"), if_true), list_of(Value("quote"), if_false)); + } + + bool is_quoted_symbol(const Value& val) { + return val.is_list() && tail(val).is_list() && head(val).is_symbol() && + head(val).get_symbol() == symbol_value("quote") && head(tail(val)).is_symbol(); + } + + void find_assigns(ref env, const Value& term, set& dests) { + if (!term.is_list()) return; + Value h = head(term); + if (h.is_symbol() && h.get_symbol() == symbol_value("#=")) { + if (tail(term).is_list() && is_quoted_symbol(head(tail(term)))) { + u64 sym = eval(env, head(tail(term))).get_symbol(); + Def* def = env->find(symbol_for(sym)); + if (def) dests.insert(sym); + } + } + if (!introduces_env(term)) { + const Value* v = &term; + while (v->is_list()) { + find_assigns(env, v->get_list().head(), dests); + v = &v->get_list().tail(); + } + } + } + + Value while_stmt(ref env, const Value& term) { + vector values = to_vector(term); + if (values.size() < 3) { + err(term.loc(), "Incorrect number of arguments for while ", "statement. Expected condition and body."); + return error(); + } + + Value body = cons(Value("do"), tail(tail(term))); + + set dests; + find_assigns(env, head(tail(term)), dests); + find_assigns(env, body, dests); + + vector nodes; + for (u64 u : dests) { + Def* def = env->find(symbol_for(u)); + if (!def->value.is_runtime()) { + Value new_value = lower(def->value); + nodes.push(new ASTDefine(new_value.loc(), env, u, new_value.get_runtime())); + def->value = new_value; + } + } + + Value cond = eval(env, head(tail(term))); + if (cond.is_error()) return error(); + if (!cond.is_runtime()) cond = lower(cond); + + body = eval(env, body); + if (body.is_error()) return error(); + if (!body.is_runtime()) body = lower(body); + nodes.push(new ASTWhile(term.loc(), cond.get_runtime(), body.get_runtime())); + return nodes.size() == 1 ? nodes[0] : new ASTBlock(term.loc(), nodes); + } + + Value list_of(ref env, const Value& term) { + Value items = tail(term); + + const Value* v = &items; + vector vals; + while (v->is_list()) { + vals.push(eval(env, v->get_list().head())); + v = &v->get_list().tail(); + } + return list_of(vals); + } + + Value tuple_of(ref env, const Value& term) { + Value items = tail(term); + + const Value* v = &items; + vector vals; + while (v->is_list()) { + vals.push(eval(env, v->get_list().head())); + v = &v->get_list().tail(); + } + return tuple_of(vals); + } + + Value array_of(ref env, const Value& term) { + Value items = tail(term); + + const Value* v = &items; + vector vals; + while (v->is_list()) { + vals.push(eval(env, v->get_list().head())); + v = &v->get_list().tail(); + } + return array_of(vals); + } + + struct CompilationUnit { + Source source; + ref env; + }; + + static map modules; + + Value use(ref env, const Value& term) { + if (!tail(term).is_list() || !head(tail(term)).is_symbol()) { + err(tail(term).loc(), "Expected symbol in use expression, given '", tail(term), "'."); + return error(); + } + Value h = head(tail(term)); + string path = symbol_for(h.get_symbol()); + path += ".bl"; + + ref module; + auto it = modules.find(path); + if (it != modules.end()) { + module = it->second.env; + } else { + CompilationUnit unit{Source((const char*)path.raw()), {}}; + if (!unit.source.begin().peek()) { + err(h.loc(), "Could not load source file at path '", path, "'."); + return error(); + } + module = unit.env = load(unit.source); + if (error_count()) return error(); + + for (const auto& p : *module) { + if (env->find(p.first)) { + err(term.loc(), "Module '", symbol_for(h.get_symbol()), "' redefines '", p.first, + "' in the current environment."); + return error(); + } + } + + modules.put(path, unit); + } + + env->import(module); + return Value(VOID); + } + + Value eval_list(ref env, const Value& term) { + Value h = head(term); + if (h.is_symbol()) { + const string& name = symbol_for(h.get_symbol()); + if (name == "quote") return head(tail(term)).clone(); + else if (name == "def") + return define(env, term, false); + else if (name == "infix") + return infix(env, term, false); + else if (name == "macro") + return define(env, term, true); + else if (name == "infix-macro") + return infix(env, term, true); + else if (name == "lambda") + return lambda(env, term); + else if (name == "do") + return do_block(env, term); + else if (name == "if") + return eval(env, if_expr(env, term)); + else if (name == "list-of") + return list_of(env, term); + else if (name == "tuple-of") + return tuple_of(env, term); + else if (name == "array-of") + return array_of(env, term); + else if (name == "use") + return use(env, term); + else if (name == "while") + return while_stmt(env, term); + } + + Value first = eval(env, h); + + if (h.is_symbol() && tail(term).is_void()) { + const Def* def = env->find(symbol_for(h.get_symbol())); + if (def->is_infix) return first; + } + + if (first.is_macro()) { + vector args; + const Value* v = &term.get_list().tail(); + u32 i = 0; + while (v->is_list()) { + if (i < first.get_macro().arity() && first.get_macro().args()[i] & KEYWORD_ARG_BIT && + v->get_list().head().is_symbol()) + args.push(list_of(Value("quote"), v->get_list().head())); + else + args.push(v->get_list().head()); + v = &v->get_list().tail(); + i++; + } + if (args.size() != first.get_macro().arity()) { + err(term.loc(), "Macro procedure expects ", first.get_macro().arity(), " arguments, ", args.size(), + " provided."); + return error(); + } + return expand(env, first, Value(new ProductValue(args))); + } else if (first.is_function() || + first.is_intersect() && ((const IntersectType*)first.type())->has_function()) { + vector args; + Value args_term = tail(term); + const Value* v = &args_term; + u32 i = 0; + while (v->is_list()) { + // if (i < first.get_function().arity() && first.get_function().args()[i] & KEYWORD_ARG_BIT && + // v->get_list().head().is_symbol()) { + // args.push(v->get_list().head()); // leave keywords quoted + // } else + args.push(eval(env, v->get_list().head())); + + v = &v->get_list().tail(); + i++; + } + return call(env, first, Value(new ProductValue(args))); + } else if (first.is_runtime() && first.get_runtime()->type()->kind() == KIND_FUNCTION) { + vector args; + Value args_term = tail(term); + const Value* v = &args_term; + u32 i = 0; + while (v->is_list()) { + args.push(eval(env, v->get_list().head())); + + v = &v->get_list().tail(); + i++; + } + const FunctionType* fntype = (const FunctionType*)first.get_runtime()->type(); + const ProductType* args_type = (const ProductType*)fntype->arg(); + if (args.size() != args_type->count()) { + err(term.loc(), "Procedure expects ", args_type->count(), " arguments, ", args.size(), " provided."); + return error(); + } + return call(env, first, Value(new ProductValue(args))); + } + + if (tail(term).is_void()) return first; + + err(term.loc(), "Could not evaluate list '", term, "'."); return error(); - } - return call(env, first, Value(new ProductValue(args))); - } - - if (tail(term).is_void()) return first; - - err(term.loc(), "Could not evaluate list '", term, "'."); - return error(); - } - - Value eval(ref env, Value term) { - if (term.is_list()) return eval_list(env, term); - else if (term.is_int()) return term; - else if (term.is_string()) return term; - else if (term.is_symbol()) { - const string& name = symbol_for(term.get_symbol()); - const Def* def = env->find(name); - if (def && def->is_macro_variable()) - return def->value.get_alias().value(); - else if (def) { - if (def->value.is_runtime()) - return new ASTVar(term.loc(), env, term.get_symbol()); - return def->value; - } - else { - err(term.loc(), "Undefined variable '", name, "'."); + } + + Value eval(ref env, Value term) { + if (term.is_list()) return eval_list(env, term); + else if (term.is_int()) + return term; + else if (term.is_string()) + return term; + else if (term.is_symbol()) { + const string& name = symbol_for(term.get_symbol()); + const Def* def = env->find(name); + if (def && def->is_macro_variable()) return def->value.get_alias().value(); + else if (def) { + if (def->value.is_runtime()) return new ASTVar(term.loc(), env, term.get_symbol()); + return def->value; + } else { + err(term.loc(), "Undefined variable '", name, "'."); + return error(); + } + } + err(term.loc(), "Could not evaluate term '", term, "'."); return error(); - } } - err(term.loc(), "Could not evaluate term '", term, "'."); - return error(); - } -} \ No newline at end of file +} // namespace basil \ No newline at end of file diff --git a/compiler/main.cpp b/compiler/main.cpp index 12be4ed..81a73eb 100644 --- a/compiler/main.cpp +++ b/compiler/main.cpp @@ -3,6 +3,7 @@ #include "values.h" #include "driver.h" #include "ast.h" +#include "builtin.h" #include "unistd.h" using namespace basil; @@ -26,51 +27,26 @@ Value repl_quit(ref env, const Value& args) { } Value print_tokens(ref env, const Value& args) { - if (!args.get_product()[0].is_bool()) { - err(args.get_product()[0].loc(), "Print tokens command requires bool, '", - args.get_product()[0], "' provided."); - return ERROR; - } basil::print_tokens(args.get_product()[0].get_bool()); return Value(VOID); } Value print_parse(ref env, const Value& args) { - if (!args.get_product()[0].is_bool()) { - err(args.get_product()[0].loc(), "Print parse tree command requires bool, '", - args.get_product()[0], "' provided."); - return ERROR; - } basil::print_parsed(args.get_product()[0].get_bool()); return Value(VOID); } Value print_ast(ref env, const Value& args) { - if (!args.get_product()[0].is_bool()) { - err(args.get_product()[0].loc(), "Print AST command requires bool, '", - args.get_product()[0], "' provided."); - return ERROR; - } basil::print_ast(args.get_product()[0].get_bool()); return Value(VOID); } Value print_ir(ref env, const Value& args) { - if (!args.get_product()[0].is_bool()) { - err(args.get_product()[0].loc(), "Print SSA command requires bool, '", - args.get_product()[0], "' provided."); - return ERROR; - } basil::print_ir(args.get_product()[0].get_bool()); return Value(VOID); } Value print_asm(ref env, const Value& args) { - if (!args.get_product()[0].is_bool()) { - err(args.get_product()[0].loc(), "Print ASM command requires bool, '", - args.get_product()[0], "' provided."); - return ERROR; - } basil::print_asm(args.get_product()[0].get_bool()); return Value(VOID); } @@ -92,6 +68,14 @@ Value repl_help(ref env, const Value& args) { int intro(); int main(int argc, char** argv) { + Builtin REPL_HELP(find(find(), VOID), repl_help, nullptr), + REPL_QUIT(find(find(), VOID), repl_quit, nullptr), + PRINT_TOKENS(find(find(BOOL), VOID), print_tokens, nullptr), + PRINT_PARSE(find(find(BOOL), VOID), print_parse, nullptr), + PRINT_AST(find(find(BOOL), VOID), print_ast, nullptr), + PRINT_IR(find(find(BOOL), VOID), print_ir, nullptr), + PRINT_ASM(find(find(BOOL), VOID), print_asm, nullptr); + if (argc == 1) { // repl mode print_banner(); println(BOLDGREEN, "Enter any Basil expression at the prompt, or '", @@ -100,13 +84,13 @@ int main(int argc, char** argv) { Source src; ref root = create_root_env(); - root->def("$help", new FunctionValue(root, repl_help, 0), 0); - root->def("$quit", new FunctionValue(root, repl_quit, 0), 0); - root->def("$print-tokens", new FunctionValue(root, print_tokens, 1), 1); - root->def("$print-parse", new FunctionValue(root, print_parse, 1), 1); - root->def("$print-ast", new FunctionValue(root, print_ast, 1), 1); - root->def("$print-ir", new FunctionValue(root, print_ir, 1), 1); - root->def("$print-asm", new FunctionValue(root, print_asm, 1), 1); + root->def("$help", Value(root, REPL_HELP), 0); + root->def("$quit", Value(root, REPL_QUIT), 0); + root->def("$print-tokens", Value(root, PRINT_TOKENS), 1); + root->def("$print-parse", Value(root, PRINT_PARSE), 1); + root->def("$print-ast", Value(root, PRINT_AST), 1); + root->def("$print-ir", Value(root, PRINT_IR), 1); + root->def("$print-asm", Value(root, PRINT_ASM), 1); ref global = newref(root); Function main_fn("main"); @@ -844,12 +828,18 @@ int intro() { Source src; + Builtin REPL_QUIT(find(find(), VOID), repl_quit, nullptr), + INTRO_HELP(find(find(), VOID), intro_help, nullptr), + INTRO_CONTENTS(find(find(), VOID), intro_contents, nullptr), + INTRO_START(find(find(), VOID), intro_start, nullptr), + INTRO_SET_SECTION(find(find(INT), VOID), intro_set_section, nullptr); + ref root = create_root_env(); - root->def("$quit", new FunctionValue(root, repl_quit, 0), 0); - root->def("$help", new FunctionValue(root, intro_help, 0), 0); - root->def("$contents", new FunctionValue(root, intro_contents, 0), 0); - root->def("$start", new FunctionValue(root, intro_start, 0), 0); - root->def("$section", new FunctionValue(root, intro_set_section, 1), 1); + root->def("$quit", Value(root, REPL_QUIT), 0); + root->def("$help", Value(root, INTRO_HELP), 0); + root->def("$contents", Value(root, INTRO_CONTENTS), 0); + root->def("$start", Value(root, INTRO_START), 0); + root->def("$section", Value(root, INTRO_SET_SECTION), 1); intro_help(root, Value(VOID)); diff --git a/compiler/type.cpp b/compiler/type.cpp index 56536d6..4573228 100644 --- a/compiler/type.cpp +++ b/compiler/type.cpp @@ -16,10 +16,10 @@ namespace basil { return this; } - bool Type::coerces_to(const Type& other) const { - return other == *this - || &other == ANY - || other.kind() == KIND_SUM && ((const SumType&)other).has(this); + bool Type::coerces_to(const Type* other) const { + return other == this + || other == ANY + || other->kind() == KIND_SUM && ((const SumType*)other)->has(this); } SingletonType::SingletonType(const string& repr) : Type(::hash(repr)), _repr(repr) {} @@ -36,6 +36,47 @@ namespace basil { write(io, _repr); } + // NumericType + + NumericType::NumericType(u32 size, bool floating): + Type(2659161638339667757ul ^ ::hash(size) ^ ::hash(floating)), + _size(size), _floating(floating) {} + + bool NumericType::floating() const { + return _floating; + } + + u32 NumericType::size() const { + return _size; + } + + TypeKind NumericType::kind() const { + return KIND_NUMERIC; + } + + bool NumericType::operator==(const Type& other) const { + return other.kind() == KIND_NUMERIC + && ((const NumericType&)other)._floating == _floating + && ((const NumericType&)other)._size == _size; + } + + bool NumericType::coerces_to(const Type* other) const { + if (Type::coerces_to(other)) return true; + if (other->kind() != KIND_NUMERIC) return false; + + bool other_floating = ((const NumericType*)other)->floating(); + u32 other_size = ((const NumericType*)other)->size(); + if (floating() == other_floating) return other_size >= size(); // both float, or both non-float + else if (!floating() && other_floating) return other_size > size(); // works for power-of-two type sizes + return false; + } + + void NumericType::format(stream& io) const { + write(io, floating() ? "f" : "i", size()); + } + + // ListType + ListType::ListType(const Type* element): Type(element->hash() ^ 11340086872871314823ul), _element(element) {} @@ -60,8 +101,8 @@ namespace basil { ((const ListType&) other).element() == element(); } - bool ListType::coerces_to(const Type& other) const { - return Type::coerces_to(other) || &other == TYPE && _element == TYPE; + bool ListType::coerces_to(const Type* other) const { + return Type::coerces_to(other) || other == TYPE && _element == TYPE; } void ListType::format(stream& io) const { @@ -110,17 +151,17 @@ namespace basil { (((const ArrayType&) other).count() == count() || !fixed()); } - bool ArrayType::coerces_to(const Type& other) const { + bool ArrayType::coerces_to(const Type* other) const { if (Type::coerces_to(other)) return true; - if (other.kind() == KIND_ARRAY - && ((const ArrayType&)other).element() == element() - && !((const ArrayType&)other).fixed()) return true; + if (other->kind() == KIND_ARRAY + && ((const ArrayType*)other)->element() == element() + && !((const ArrayType*)other)->fixed()) return true; - if (fixed() && other.kind() == KIND_PRODUCT - && ((const ProductType&)other).count() == count()) { + if (fixed() && other->kind() == KIND_PRODUCT + && ((const ProductType*)other)->count() == count()) { for (u32 i = 0; i < count(); i ++) - if (((const ProductType&)other).member(i) != element()) return false; + if (((const ProductType*)other)->member(i) != element()) return false; return true; } @@ -132,7 +173,6 @@ namespace basil { else write(io, _element, "[]"); } - u64 set_hash(const set& members) { u64 h = 6530804687830202173ul; for (const Type* t : members) h ^= t->hash(); @@ -140,7 +180,7 @@ namespace basil { } SumType::SumType(const set& members): - Type(set_hash(members)), _members(members) {} + Type(2853124965035107823ul ^ set_hash(members)), _members(members) {} bool SumType::has(const Type* member) const { return _members.find(member) != _members.end(); @@ -157,12 +197,12 @@ namespace basil { return true; } - bool SumType::coerces_to(const Type& other) const { + bool SumType::coerces_to(const Type* other) const { if (Type::coerces_to(other)) return true; - if (other.kind() == KIND_SUM) { + if (other->kind() == KIND_SUM) { for (const Type* t : _members) - if (!((const SumType&)other).has(t)) return false; + if (!((const SumType*)other)->has(t)) return false; return true; } @@ -179,6 +219,51 @@ namespace basil { write(io, ")"); } + IntersectType::IntersectType(const set& members): + Type(15263450813870290249ul ^ set_hash(members)), _members(members), _has_function(false) { + for (const Type* t : _members) if (t->kind() == KIND_FUNCTION) _has_function = true; + } + + bool IntersectType::has(const Type* member) const { + return _members.find(member) != _members.end(); + } + + bool IntersectType::has_function() const { + return _has_function; + } + + TypeKind IntersectType::kind() const { + return KIND_INTERSECT; + } + + bool IntersectType::operator==(const Type& other) const { + if (other.kind() != kind()) return false; + for (const Type *t : _members) + if (!((const IntersectType&)other).has(t)) return false; + return true; + } + + bool IntersectType::coerces_to(const Type* other) const { + if (Type::coerces_to(other)) return true; + if (has(other)) return true; + if (other->kind() == KIND_INTERSECT) { + for (const Type* t : ((const IntersectType*)other)->_members) + if (!has(t)) return false; + return true; + } + return false; + } + + void IntersectType::format(stream& io) const { + write(io, "("); + bool first = true; + for (const Type* t : _members) { + write(io, first ? "" : " & ", t); + first = false; + } + write(io, ")"); + } + u64 vector_hash(const vector& members) { u64 h = 10472618355682807153ul; for (const Type* t : members) h ^= t->hash(); @@ -221,23 +306,23 @@ namespace basil { return true; } - bool ProductType::coerces_to(const Type& other) const { + bool ProductType::coerces_to(const Type* other) const { if (Type::coerces_to(other)) return true; // if (other.kind() == KIND_ARRAY // && ((const ArrayType&)other).element() == element() // && !((const ArrayType&)other).fixed()) return true; - if (&other == TYPE) { + if (other == TYPE) { for (u32 i = 0; i < count(); i ++) if (member(i) != TYPE) return false; return true; } - if (other.kind() == KIND_ARRAY && ((const ArrayType&)other).fixed() - && ((const ArrayType&)other).count() == count()) { + if (other->kind() == KIND_ARRAY && ((const ArrayType*)other)->fixed() + && ((const ArrayType*)other)->count() == count()) { for (u32 i = 0; i < count(); i ++) - if (member(i) != ((const ArrayType&)other).element()) return false; + if (member(i) != ((const ArrayType*)other)->element()) return false; return true; } @@ -424,7 +509,8 @@ namespace basil { return t; } - const Type *INT = find("int"), + const Type *INT = find(64, false), + *FLOAT = find(64, true), *SYMBOL = find("symbol"), *VOID = find("void"), *ERROR = find("error"), @@ -495,8 +581,8 @@ namespace basil { if (b == VOID && a->kind() == KIND_LIST) return a; if (coercing || converting) { - if (a->coerces_to(*b)) return b; - if (b->coerces_to(*a)) return a; + if (a->coerces_to(b)) return b; + if (b->coerces_to(a)) return a; } if (a != b) return nullptr; diff --git a/compiler/type.h b/compiler/type.h index 34c8ac4..5ef03f1 100644 --- a/compiler/type.h +++ b/compiler/type.h @@ -11,16 +11,18 @@ namespace basil { const u8 GC_KIND_FLAG = 128; enum TypeKind : u8 { - KIND_SINGLETON = 0, + KIND_SINGLETON = 0, KIND_TYPEVAR = 1, - KIND_LIST = GC_KIND_FLAG | 0, + KIND_NUMERIC = 2, + KIND_LIST = GC_KIND_FLAG | 0, KIND_SUM = GC_KIND_FLAG | 1, - KIND_PRODUCT = GC_KIND_FLAG | 2, - KIND_ARRAY = GC_KIND_FLAG | 3, - KIND_FUNCTION = GC_KIND_FLAG | 4, - KIND_ALIAS = GC_KIND_FLAG | 5, - KIND_MACRO = GC_KIND_FLAG | 6, - KIND_RUNTIME = GC_KIND_FLAG | 7 + KIND_INTERSECT = GC_KIND_FLAG | 2, + KIND_PRODUCT = GC_KIND_FLAG | 3, + KIND_ARRAY = GC_KIND_FLAG | 4, + KIND_FUNCTION = GC_KIND_FLAG | 5, + KIND_ALIAS = GC_KIND_FLAG | 6, + KIND_MACRO = GC_KIND_FLAG | 7, + KIND_RUNTIME = GC_KIND_FLAG | 8 }; class Type { @@ -34,7 +36,7 @@ namespace basil { virtual bool concrete() const; virtual const Type* concretify() const; virtual bool operator==(const Type& other) const = 0; - virtual bool coerces_to(const Type& other) const; + virtual bool coerces_to(const Type* other) const; virtual void format(stream& io) const = 0; }; @@ -48,6 +50,20 @@ namespace basil { void format(stream& io) const override; }; + class NumericType : public Type { + u32 _size; + bool _floating; + public: + NumericType(u32 size, bool floating); + + bool floating() const; + u32 size() const; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + bool coerces_to(const Type* other) const override; + void format(stream& io) const override; + }; + class ListType : public Type { const Type* _element; public: @@ -58,7 +74,7 @@ namespace basil { const Type* concretify() const override; TypeKind kind() const override; bool operator==(const Type& other) const override; - bool coerces_to(const Type& other) const override; + bool coerces_to(const Type* other) const override; void format(stream& io) const override; }; @@ -77,7 +93,7 @@ namespace basil { const Type* concretify() const override; TypeKind kind() const override; bool operator==(const Type& other) const override; - bool coerces_to(const Type& other) const override; + bool coerces_to(const Type* other) const override; void format(stream& io) const override; }; @@ -89,13 +105,31 @@ namespace basil { bool has(const Type* member) const; TypeKind kind() const override; bool operator==(const Type& other) const override; - bool coerces_to(const Type& other) const override; + bool coerces_to(const Type* other) const override; + void format(stream& io) const override; + }; + + class IntersectType : public Type { + set _members; + bool _has_function; + public: + template + IntersectType(const Args&... args): IntersectType(set_of(args...)) {} + IntersectType(const set& members); + + bool has(const Type* member) const; + bool has_function() const; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + bool coerces_to(const Type* other) const override; void format(stream& io) const override; }; class ProductType : public Type { vector _members; public: + template + ProductType(const Args&... args): ProductType(vector_of(args...)) {} ProductType(const vector& members); u32 count() const; @@ -104,7 +138,7 @@ namespace basil { const Type* concretify() const override; TypeKind kind() const override; bool operator==(const Type& other) const override; - bool coerces_to(const Type& other) const override; + bool coerces_to(const Type* other) const override; void format(stream& io) const override; }; @@ -184,7 +218,7 @@ namespace basil { return create_type(new T(t)); } - extern const Type *INT, *SYMBOL, *VOID, *ERROR, *TYPE, + extern const Type *INT, *FLOAT, *SYMBOL, *VOID, *ERROR, *TYPE, *ALIAS, *BOOL, *ANY, *STRING; const Type* unify(const Type* a, const Type* b, bool coercing = false, bool converting = false); diff --git a/compiler/values.cpp b/compiler/values.cpp index 722a5f1..5f29a58 100644 --- a/compiler/values.cpp +++ b/compiler/values.cpp @@ -1,1479 +1,1589 @@ #include "values.h" -#include "util/vec.h" +#include "ast.h" +#include "builtin.h" #include "env.h" #include "eval.h" -#include "ast.h" #include "native.h" +#include "util/vec.h" namespace basil { - static map symbol_table; - static vector symbol_array; - - u64 symbol_value(const string& symbol) { - static u64 count = 0; - auto it = symbol_table.find(symbol); - if (it == symbol_table.end()) { - symbol_table.put(symbol, count++); - symbol_array.push(symbol); - return count - 1; - } - else return it->second; - } - - const string& symbol_for(u64 value) { - return symbol_array[value]; - } - - Value::Value(): Value(VOID) {} - - Value::Value(const Type* type): - _type(type) {} - - Value::Value(i64 i, const Type* type): - _type(type) { - is_bool() ? _data.b = i : _data.i = i; - } - - Value::Value(const string& s, const Type* type): - _type(type) { - if (type == SYMBOL) _data.u = symbol_value(s); - else if (type == STRING) _data.rc = new StringValue(s); - } - - Value::Value(const Type* type_value, const Type* type): - _type(type) { - _data.t = type_value; - } - - Value::Value(ListValue* l): - _type(find(l->head().type())) { - _data.rc = l; - } - - Value::Value(SumValue* s, const Type* type): - _type(type) { - _data.rc = s; - } - - Value::Value(ProductValue* p) { - vector ts; - for (const Value& v : *p) ts.push(v.type()); - _type = find(ts); - _data.rc = p; - } - - Value::Value(ArrayValue* a) { - set ts; - for (const Value& v : *a) ts.insert(v.type()); - _type = find(find(ts), a->size()); - _data.rc = a; - } - - Value::Value(FunctionValue* f): - _type(f->is_builtin() ? find(ANY, ANY) - : find(find(), find())) { - _data.rc = f; - } - - Value::Value(AliasValue* a): - _type(ALIAS) { - _data.rc = a; - } - - Value::Value(MacroValue* m): - _type(find(m->arity())) { - _data.rc = m; - } - - Value::Value(ASTNode* n): - _type(find(n->type())) { - _data.rc = n; - } - - Value::~Value() { - if (_type->kind() & GC_KIND_FLAG) _data.rc->dec(); - } - - Value::Value(const Value& other): - _type(other._type), _loc(other._loc) { - _data.u = other._data.u; // copy over raw data - if (_type->kind() & GC_KIND_FLAG) _data.rc->inc(); - } - - Value& Value::operator=(const Value& other) { - if (other.type()->kind() & GC_KIND_FLAG) other._data.rc->inc(); - if (_type->kind() & GC_KIND_FLAG) _data.rc->dec(); - _type = other._type; - _loc = other._loc; - _data.u = other._data.u; // copy over raw data - return *this; - } - - bool Value::is_int() const { - return _type == INT; - } - - i64 Value::get_int() const { - return _data.i; - } - - i64& Value::get_int() { - return _data.i; - } - - bool Value::is_symbol() const { - return _type == SYMBOL; - } - - u64 Value::get_symbol() const { - return _data.u; - } - - u64& Value::get_symbol() { - return _data.u; - } - - bool Value::is_string() const { - return _type == STRING; - } - - const string& Value::get_string() const { - return ((const StringValue*)_data.rc)->value(); - } - - string& Value::get_string() { - return ((StringValue*)_data.rc)->value(); - } - - bool Value::is_void() const { - return _type == VOID; - } - - bool Value::is_error() const { - return _type == ERROR; - } - - bool Value::is_type() const { - return _type == TYPE; - } - - const Type* Value::get_type() const { - return _data.t; - } - - const Type*& Value::get_type() { - return _data.t; - } - - bool Value::is_bool() const { - return _type == BOOL; - } - - bool Value::get_bool() const { - return _data.b; - } - - bool& Value::get_bool() { - return _data.b; - } - - bool Value::is_list() const { - return _type->kind() == KIND_LIST; - } - - const ListValue& Value::get_list() const { - return *(const ListValue*)_data.rc; - } - - ListValue& Value::get_list() { - return *(ListValue*)_data.rc; - } - - bool Value::is_array() const { - return _type->kind() == KIND_ARRAY; - } - - const ArrayValue& Value::get_array() const { - return *(const ArrayValue*)_data.rc; - } - - ArrayValue& Value::get_array() { - return *(ArrayValue*)_data.rc; - } - - bool Value::is_sum() const { - return _type->kind() == KIND_SUM; - } - - const SumValue& Value::get_sum() const { - return *(const SumValue*)_data.rc; - } - - SumValue& Value::get_sum() { - return *(SumValue*)_data.rc; - } - - bool Value::is_product() const { - return _type->kind() == KIND_PRODUCT; - } - - const ProductValue& Value::get_product() const { - return *(const ProductValue*)_data.rc; - } - - ProductValue& Value::get_product() { - return *(ProductValue*)_data.rc; - } - - bool Value::is_function() const { - return _type->kind() == KIND_FUNCTION; - } - - const FunctionValue& Value::get_function() const { - return *(const FunctionValue*)_data.rc; - } - - FunctionValue& Value::get_function() { - return *(FunctionValue*)_data.rc; - } - - bool Value::is_alias() const { - return _type->kind() == KIND_ALIAS; - } - - const AliasValue& Value::get_alias() const { - return *(const AliasValue*)_data.rc; - } - - AliasValue& Value::get_alias() { - return *(AliasValue*)_data.rc; - } - - bool Value::is_macro() const { - return _type->kind() == KIND_MACRO; - } - - const MacroValue& Value::get_macro() const { - return *(const MacroValue*)_data.rc; - } - - MacroValue& Value::get_macro() { - return *(MacroValue*)_data.rc; - } - - bool Value::is_runtime() const { - return _type->kind() == KIND_RUNTIME; - } - - ASTNode* Value::get_runtime() const { - return (ASTNode*)_data.rc; - } - - ASTNode*& Value::get_runtime() { - return (ASTNode*&)_data.rc; - } - - const Type* Value::type() const { - return _type; - } - - void Value::format(stream& io) const { - if (is_void()) write(io, "()"); - else if (is_error()) write(io, "error"); - else if (is_int()) write(io, get_int()); - else if (is_symbol()) write(io, symbol_for(get_symbol())); - else if (is_string()) write(io, '"', get_string(), '"'); - else if (is_type()) write(io, get_type()); - else if (is_bool()) write(io, get_bool()); - else if (is_list()) { - bool first = true; - write(io, "("); - const Value* ptr = this; - while (ptr->is_list()) { - write(io, first ? "" : " ", ptr->get_list().head()); - ptr = &ptr->get_list().tail(); - first = false; - } - write(io, ")"); - } - else if (is_array()) { - bool first = true; - write(io, "["); - for (const Value& v : get_array()) { - write(io, first ? "" : ", ", v); - first = false; - } - write(io, "]"); - } - else if (is_sum()) write(io, get_sum().value()); - else if (is_product()) { - bool first = true; - write(io, "("); - for (const Value& v : get_product()) { - write(io, first ? "" : ", ", v); - first = false; - } - write(io, ")"); - } - else if (is_function()) write(io, "<#procedure>"); - else if (is_alias()) write(io, "<#alias>"); - else if (is_macro()) write(io, "<#macro>"); - else if (is_runtime()) - write(io, "<#runtime ", ((const RuntimeType*)_type)->base(), ">"); - } - - u64 Value::hash() const { - if (is_void()) return 11103515024943898793ul; - else if (is_error()) return 14933118315469276343ul; - else if (is_int()) return ::hash(get_int()) ^ 6909969109598810741ul; - else if (is_symbol()) return ::hash(get_symbol()) ^ 1899430078708870091ul; - else if (is_string()) return ::hash(get_string()) ^ 1276873522146073541ul; - else if (is_type()) return get_type()->hash(); - else if (is_bool()) - return get_bool() ? 9269586835432337327ul - : 18442604092978916717ul; - else if (is_list()) { - u64 h = 9572917161082946201ul; - Value ptr = *this; - while (ptr.is_list()) { - h ^= ptr.get_list().head().hash(); - ptr = ptr.get_list().tail(); - } - return h; - } - else if (is_sum()) { - return get_sum().value().hash() ^ 7458465441398727979ul; - } - else if (is_product()) { - u64 h = 16629385277682082909ul; - for (const Value& v : get_product()) h ^= v.hash(); - return h; - } - else if (is_array()) { - u64 h = 7135911592309895053ul; - for (const Value& v : get_array()) h ^= v.hash(); - return h; - } - else if (is_function()) { - u64 h = 10916307465547805281ul; - if (get_function().is_builtin()) - h ^= ::hash(get_function().get_builtin()); - else { - h ^= get_function().body().hash(); - for (u64 arg : get_function().args()) - h ^= ::hash(arg); - } - return h; - } - else if (is_alias()) return 6860110315984869641ul; - else if (is_macro()) { - u64 h = 16414641732770006573ul; - if (get_macro().is_builtin()) - h ^= ::hash(get_macro().get_builtin()); - else { - h ^= get_macro().body().hash(); - for (u64 arg : get_macro().args()) - h ^= ::hash(arg); - } - return h; - } - else if (is_runtime()) { - return _type->hash() ^ ::hash(_data.rc); - } - return 0; - } - - bool Value::operator==(const Value& other) const { - if (type() != other.type()) return false; - else if (is_int()) return get_int() == other.get_int(); - else if (is_symbol()) return get_symbol() == other.get_symbol(); - else if (is_type()) return get_type() == other.get_type(); - else if (is_bool()) return get_bool() == other.get_bool(); - else if (is_string()) return get_string() == other.get_string(); - else if (is_product()) { - if (get_product().size() != other.get_product().size()) return false; - for (u32 i = 0; i < get_product().size(); i ++) - if (get_product()[i] != other.get_product()[i]) return false; - return true; - } - else if (is_array()) { - if (get_array().size() != other.get_array().size()) return false; - for (u32 i = 0; i < get_array().size(); i ++) - if (get_array()[i] != other.get_array()[i]) return false; - return true; - } - else if (is_list()) { - const Value* l = this, *o = &other; - while (l->is_list() && o->is_list()) { - if (l->get_list().head() != o->get_list().head()) return false; - l = &l->get_list().tail(), o = &o->get_list().tail(); - } - return l->is_void() && o->is_void(); - } - else if (is_function()) { - if (get_function().is_builtin()) - return get_function().get_builtin() == - other.get_function().get_builtin(); - else { - if (other.get_function().arity() != get_function().arity()) - return false; - for (u32 i = 0; i < get_function().arity(); i ++) { - if (other.get_function().args()[i] != - get_function().args()[i]) return false; + static map symbol_table; + static vector symbol_array; + + u64 symbol_value(const string& symbol) { + static u64 count = 0; + auto it = symbol_table.find(symbol); + if (it == symbol_table.end()) { + symbol_table.put(symbol, count++); + symbol_array.push(symbol); + return count - 1; + } else + return it->second; + } + + const string& symbol_for(u64 value) { + return symbol_array[value]; + } + + Value::Value() : Value(VOID) {} + + Value::Value(const Type* type) : _type(type) {} + + Value::Value(i64 i, const Type* type) : _type(type) { + is_bool() ? _data.b = i : _data.i = i; + } + + Value::Value(const string& s, const Type* type) : _type(type) { + if (type == SYMBOL) _data.u = symbol_value(s); + else if (type == STRING) + _data.rc = new StringValue(s); + } + + Value::Value(const Type* type_value, const Type* type) : _type(type) { + _data.t = type_value; + } + + Value::Value(ListValue* l) : _type(find(l->head().type())) { + _data.rc = l; + } + + Value::Value(SumValue* s, const Type* type) : _type(type) { + _data.rc = s; + } + + Value::Value(IntersectValue* i, const Type* type) : _type(type) { + _data.rc = i; + } + + Value::Value(ProductValue* p) { + vector ts; + for (const Value& v : *p) ts.push(v.type()); + _type = find(ts); + _data.rc = p; + } + + Value::Value(ArrayValue* a) { + set ts; + for (const Value& v : *a) ts.insert(v.type()); + _type = find(find(ts), a->size()); + _data.rc = a; + } + + Value::Value(ref env, const Builtin& b) : _type(b.type()) { + if (b.type()->kind() == KIND_FUNCTION) _data.rc = new FunctionValue(env, b); + else + _data.rc = new MacroValue(env, b); + } + + Value::Value(FunctionValue* f, const Type* ftype) : _type(ftype) { + _data.rc = f; + } + + Value::Value(AliasValue* a) : _type(ALIAS) { + _data.rc = a; + } + + Value::Value(MacroValue* m) : _type(find(m->arity())) { + _data.rc = m; + } + + Value::Value(ASTNode* n) : _type(find(n->type())) { + _data.rc = n; + } + + Value::~Value() { + if (_type->kind() & GC_KIND_FLAG) _data.rc->dec(); + } + + Value::Value(const Value& other) : _type(other._type), _loc(other._loc) { + _data.u = other._data.u; // copy over raw data + if (_type->kind() & GC_KIND_FLAG) _data.rc->inc(); + } + + Value& Value::operator=(const Value& other) { + if (other.type()->kind() & GC_KIND_FLAG) other._data.rc->inc(); + if (_type->kind() & GC_KIND_FLAG) _data.rc->dec(); + _type = other._type; + _loc = other._loc; + _data.u = other._data.u; // copy over raw data + return *this; + } + + bool Value::is_int() const { + return _type == INT; + } + + i64 Value::get_int() const { + return _data.i; + } + + i64& Value::get_int() { + return _data.i; + } + + bool Value::is_symbol() const { + return _type == SYMBOL; + } + + u64 Value::get_symbol() const { + return _data.u; + } + + u64& Value::get_symbol() { + return _data.u; + } + + bool Value::is_string() const { + return _type == STRING; + } + + const string& Value::get_string() const { + return ((const StringValue*)_data.rc)->value(); + } + + string& Value::get_string() { + return ((StringValue*)_data.rc)->value(); + } + + bool Value::is_void() const { + return _type == VOID; + } + + bool Value::is_error() const { + return _type == ERROR; + } + + bool Value::is_type() const { + return _type == TYPE; + } + + const Type* Value::get_type() const { + return _data.t; + } + + const Type*& Value::get_type() { + return _data.t; + } + + bool Value::is_bool() const { + return _type == BOOL; + } + + bool Value::get_bool() const { + return _data.b; + } + + bool& Value::get_bool() { + return _data.b; + } + + bool Value::is_list() const { + return _type->kind() == KIND_LIST; + } + + const ListValue& Value::get_list() const { + return *(const ListValue*)_data.rc; + } + + ListValue& Value::get_list() { + return *(ListValue*)_data.rc; + } + + bool Value::is_array() const { + return _type->kind() == KIND_ARRAY; + } + + const ArrayValue& Value::get_array() const { + return *(const ArrayValue*)_data.rc; + } + + ArrayValue& Value::get_array() { + return *(ArrayValue*)_data.rc; + } + + bool Value::is_sum() const { + return _type->kind() == KIND_SUM; + } + + const SumValue& Value::get_sum() const { + return *(const SumValue*)_data.rc; + } + + SumValue& Value::get_sum() { + return *(SumValue*)_data.rc; + } + + bool Value::is_intersect() const { + return _type->kind() == KIND_INTERSECT; + } + + const IntersectValue& Value::get_intersect() const { + return *(const IntersectValue*)_data.rc; + } + + IntersectValue& Value::get_intersect() { + return *(IntersectValue*)_data.rc; + } + + bool Value::is_product() const { + return _type->kind() == KIND_PRODUCT; + } + + const ProductValue& Value::get_product() const { + return *(const ProductValue*)_data.rc; + } + + ProductValue& Value::get_product() { + return *(ProductValue*)_data.rc; + } + + bool Value::is_function() const { + return _type->kind() == KIND_FUNCTION; + } + + const FunctionValue& Value::get_function() const { + return *(const FunctionValue*)_data.rc; + } + + FunctionValue& Value::get_function() { + return *(FunctionValue*)_data.rc; + } + + bool Value::is_alias() const { + return _type->kind() == KIND_ALIAS; + } + + const AliasValue& Value::get_alias() const { + return *(const AliasValue*)_data.rc; + } + + AliasValue& Value::get_alias() { + return *(AliasValue*)_data.rc; + } + + bool Value::is_macro() const { + return _type->kind() == KIND_MACRO; + } + + const MacroValue& Value::get_macro() const { + return *(const MacroValue*)_data.rc; + } + + MacroValue& Value::get_macro() { + return *(MacroValue*)_data.rc; + } + + bool Value::is_runtime() const { + return _type->kind() == KIND_RUNTIME; + } + + ASTNode* Value::get_runtime() const { + return (ASTNode*)_data.rc; + } + + ASTNode*& Value::get_runtime() { + return (ASTNode*&)_data.rc; + } + + const Type* Value::type() const { + return _type; + } + + void Value::format(stream& io) const { + if (is_void()) write(io, "()"); + else if (is_error()) + write(io, "error"); + else if (is_int()) + write(io, get_int()); + else if (is_symbol()) + write(io, symbol_for(get_symbol())); + else if (is_string()) + write(io, '"', get_string(), '"'); + else if (is_type()) + write(io, get_type()); + else if (is_bool()) + write(io, get_bool()); + else if (is_list()) { + bool first = true; + write(io, "("); + const Value* ptr = this; + while (ptr->is_list()) { + write(io, first ? "" : " ", ptr->get_list().head()); + ptr = &ptr->get_list().tail(); + first = false; + } + write(io, ")"); + } else if (is_array()) { + bool first = true; + write(io, "["); + for (const Value& v : get_array()) { + write(io, first ? "" : ", ", v); + first = false; + } + write(io, "]"); + } else if (is_sum()) + write(io, get_sum().value()); + else if (is_intersect()) { + bool first = true; + write(io, "("); + for (const auto& p : get_intersect()) { + write(io, first ? "" : " & ", p.second); + first = false; + } + write(io, ")"); + } else if (is_product()) { + bool first = true; + write(io, "("); + for (const Value& v : get_product()) { + write(io, first ? "" : ", ", v); + first = false; + } + write(io, ")"); + } else if (is_function()) + write(io, "<#procedure>"); + else if (is_alias()) + write(io, "<#alias>"); + else if (is_macro()) + write(io, "<#macro>"); + else if (is_runtime()) + write(io, "<#runtime ", ((const RuntimeType*)_type)->base(), ">"); + } + + u64 Value::hash() const { + if (is_void()) return 11103515024943898793ul; + else if (is_error()) + return 14933118315469276343ul; + else if (is_int()) + return ::hash(get_int()) ^ 6909969109598810741ul; + else if (is_symbol()) + return ::hash(get_symbol()) ^ 1899430078708870091ul; + else if (is_string()) + return ::hash(get_string()) ^ 1276873522146073541ul; + else if (is_type()) + return get_type()->hash(); + else if (is_bool()) + return get_bool() ? 9269586835432337327ul : 18442604092978916717ul; + else if (is_list()) { + u64 h = 9572917161082946201ul; + Value ptr = *this; + while (ptr.is_list()) { + h ^= ptr.get_list().head().hash(); + ptr = ptr.get_list().tail(); + } + return h; + } else if (is_sum()) { + return get_sum().value().hash() ^ 7458465441398727979ul; + } else if (is_intersect()) { + u64 h = 1250849227517037781ul; + for (const auto& p : get_intersect()) h ^= p.second.hash(); + return h; + } else if (is_product()) { + u64 h = 16629385277682082909ul; + for (const Value& v : get_product()) h ^= v.hash(); + return h; + } else if (is_array()) { + u64 h = 7135911592309895053ul; + for (const Value& v : get_array()) h ^= v.hash(); + return h; + } else if (is_function()) { + u64 h = 10916307465547805281ul; + if (get_function().is_builtin()) h ^= ::hash(&get_function().get_builtin()); + else { + h ^= get_function().body().hash(); + for (u64 arg : get_function().args()) h ^= ::hash(arg); + } + return h; + } else if (is_alias()) + return 6860110315984869641ul; + else if (is_macro()) { + u64 h = 16414641732770006573ul; + if (get_macro().is_builtin()) h ^= ::hash(&get_macro().get_builtin()); + else { + h ^= get_macro().body().hash(); + for (u64 arg : get_macro().args()) h ^= ::hash(arg); + } + return h; + } else if (is_runtime()) { + return _type->hash() ^ ::hash(_data.rc); } - return get_function().body() == other.get_function().body(); - } - } - else if (is_macro()) { - if (get_macro().is_builtin()) - return get_macro().get_builtin() == - other.get_macro().get_builtin(); - else { - if (other.get_macro().arity() != get_macro().arity()) - return false; - for (u32 i = 0; i < get_macro().arity(); i ++) { - if (other.get_macro().args()[i] != - get_macro().args()[i]) return false; + return 0; + } + + bool Value::operator==(const Value& other) const { + if (type() != other.type()) return false; + else if (is_int()) + return get_int() == other.get_int(); + else if (is_symbol()) + return get_symbol() == other.get_symbol(); + else if (is_type()) + return get_type() == other.get_type(); + else if (is_bool()) + return get_bool() == other.get_bool(); + else if (is_string()) + return get_string() == other.get_string(); + else if (is_sum()) + return get_sum().value() == other.get_sum().value(); + else if (is_intersect()) { + if (get_intersect().size() != other.get_intersect().size()) return false; + for (const auto& p : get_intersect()) { + if (!other.get_intersect().has(p.first)) return false; + if (p.second != other.get_intersect().values()[p.first]) return false; + } + return true; + } else if (is_product()) { + if (get_product().size() != other.get_product().size()) return false; + for (u32 i = 0; i < get_product().size(); i++) + if (get_product()[i] != other.get_product()[i]) return false; + return true; + } else if (is_array()) { + if (get_array().size() != other.get_array().size()) return false; + for (u32 i = 0; i < get_array().size(); i++) + if (get_array()[i] != other.get_array()[i]) return false; + return true; + } else if (is_list()) { + const Value *l = this, *o = &other; + while (l->is_list() && o->is_list()) { + if (l->get_list().head() != o->get_list().head()) return false; + l = &l->get_list().tail(), o = &o->get_list().tail(); + } + return l->is_void() && o->is_void(); + } else if (is_function()) { + if (get_function().is_builtin()) + return &get_function().get_builtin() == &other.get_function().get_builtin(); + else { + if (other.get_function().arity() != get_function().arity()) return false; + for (u32 i = 0; i < get_function().arity(); i++) { + if (other.get_function().args()[i] != get_function().args()[i]) return false; + } + return get_function().body() == other.get_function().body(); + } + } else if (is_macro()) { + if (get_macro().is_builtin()) return &get_macro().get_builtin() == &other.get_macro().get_builtin(); + else { + if (other.get_macro().arity() != get_macro().arity()) return false; + for (u32 i = 0; i < get_macro().arity(); i++) { + if (other.get_macro().args()[i] != get_macro().args()[i]) return false; + } + return get_macro().body() == other.get_macro().body(); + } + } else if (is_runtime()) { + return _data.rc == other._data.rc; } - return get_macro().body() == other.get_macro().body(); - } - } - else if (is_runtime()) { - return _data.rc == other._data.rc; - } - return type() == other.type(); - } - - Value Value::clone() const { - if (is_list()) - return Value(new ListValue(get_list().head().clone(), - get_list().tail().clone())); - else if (is_string()) - return Value(get_string(), STRING); - else if (is_sum()) - return Value(new SumValue(get_sum().value()), type()); - else if (is_product()) { - vector values; - for (const Value& v : get_product()) values.push(v); - return Value(new ProductValue(values)); - } - else if (is_array()) { - vector values; - for (const Value& v : get_array()) values.push(v); - return Value(new ProductValue(values)); - } - else if (is_function()) { - if (get_function().is_builtin()) { - return Value(new FunctionValue(get_function().get_env()->clone(), - get_function().get_builtin(), get_function().arity())); - } - else { - return Value(new FunctionValue(get_function().get_env()->clone(), - get_function().args(), get_function().body().clone())); - } - } - else if (is_alias()) - return Value(new AliasValue(get_alias().value())); - else if (is_macro()) { - if (get_macro().is_builtin()) { - return Value(new MacroValue(get_macro().get_env()->clone(), - get_macro().get_builtin(), get_macro().arity())); - } - else { - return Value(new MacroValue(get_macro().get_env()->clone(), - get_macro().args(), get_macro().body().clone())); - } - } - else if (is_runtime()) { - // todo: ast cloning - } - return *this; - } - - bool Value::operator!=(const Value& other) const { - return !(*this == other); - } - - void Value::set_location(SourceLocation loc) { - _loc = loc; - } - - SourceLocation Value::loc() const { - return _loc; - } - - StringValue::StringValue(const string& value): - _value(value) {} - - string& StringValue::value() { - return _value; - } - - const string& StringValue::value() const { - return _value; - } - - ListValue::ListValue(const Value& head, const Value& tail) : - _head(head), _tail(tail) {} - - Value& ListValue::head() { - return _head; - } - - const Value& ListValue::head() const { - return _head; - } - - Value& ListValue::tail() { - return _tail; - } - - const Value& ListValue::tail() const { - return _tail; - } - - SumValue::SumValue(const Value& value): - _value(value) {} - - Value& SumValue::value() { - return _value; - } - - const Value& SumValue::value() const { - return _value; - } - - ProductValue::ProductValue(const vector& values): - _values(values) {} - - u32 ProductValue::size() const { - return _values.size(); - } - - Value& ProductValue::operator[](u32 i) { - return _values[i]; - } - - const Value& ProductValue::operator[](u32 i) const { - return _values[i]; - } - - const Value* ProductValue::begin() const { - return _values.begin(); - } - - const Value* ProductValue::end() const { - return _values.end(); - } - - Value* ProductValue::begin() { - return _values.begin(); - } - - Value* ProductValue::end() { - return _values.end(); - } - - const vector& ProductValue::values() const { - return _values; - } - - ArrayValue::ArrayValue(const vector& values): - ProductValue(values) {} - - const u64 KEYWORD_ARG_BIT = 1ul << 63; - const u64 ARG_NAME_MASK = ~KEYWORD_ARG_BIT; - - FunctionValue::FunctionValue(ref env, const vector& args, - const Value& code, i64 name): - _name(name), _code(code), _builtin(nullptr), - _env(env), _args(args), _insts(nullptr), _calls(nullptr) {} - - FunctionValue::FunctionValue(ref env, BuiltinFn builtin, - u64 arity, i64 name): - _name(name), _builtin(builtin), _env(env), - _builtin_arity(arity), _insts(nullptr), _calls(nullptr) {} - - FunctionValue::~FunctionValue() { - if (_insts) { - for (auto& p : *_insts) p.second->dec(); - delete _insts; - } - if (_calls) { - for (auto& p : *_calls) ((FunctionValue*)p)->dec(); - delete _calls; - } - } - - FunctionValue::FunctionValue(const FunctionValue& other): - _name(other._name), _code(other._code.clone()), - _builtin(other._builtin), _env(other._env), _args(other._args), - _insts(other._insts ? - new map(*other._insts) - : nullptr), - _calls(other._calls ? - new set(*other._calls) : nullptr) { - if (_insts) for (auto& p : *_insts) p.second->inc(); - if (_calls) for (auto p : *_calls) ((FunctionValue*)p)->inc(); - } - - FunctionValue& FunctionValue::operator=(const FunctionValue& other) { - if (this != &other) { - if (_insts) { - for (auto& p : *_insts) p.second->dec(); - delete _insts; - } - if (_calls) { - for (auto& p : *_calls) ((FunctionValue*)p)->dec(); - delete _calls; - } - _name = other._name; - _code = other._code.clone(); - _builtin = other._builtin; - _env = other._env; - _args = other._args; - _insts = other._insts ? - new map(*other._insts) - : nullptr; - _calls = other._calls ? - new set(*other._calls) : nullptr; - if (_insts) for (auto& p : *_insts) p.second->inc(); - if (_calls) for (auto& p : *_calls) ((FunctionValue*)p)->inc(); - } - return *this; - } - - const vector& FunctionValue::args() const { - return _args; - } - - bool FunctionValue::is_builtin() const { - return _builtin; - } - - BuiltinFn FunctionValue::get_builtin() const { - return _builtin; - } - - ref FunctionValue::get_env() { - return _env; - } - - const ref FunctionValue::get_env() const { - return _env; - } - - i64 FunctionValue::name() const { - return _name; - } - - bool FunctionValue::found_calls() const { - return _calls; - } - - bool FunctionValue::recursive() const { - return _calls && _calls->find(this) != _calls->end(); - } - - void FunctionValue::add_call(const FunctionValue* other) { - if (!_calls) _calls = new set(); - if (other != this && other->_calls) - for (const FunctionValue* f : *other->_calls) { - _calls->insert(f); - ((FunctionValue*)f)->inc(); // evil - } - _calls->insert(other); - ((FunctionValue*)other)->inc(); - } - - ASTNode* FunctionValue::instantiation(const Type* type) const { - if (_insts) { - auto it = _insts->find(type); - if (it != _insts->end()) return it->second; - } - return nullptr; - } - - void FunctionValue::instantiate(const Type* type, ASTNode* body) { - if (!_insts) _insts = new map(); - auto it = _insts->find(type); - if (it == _insts->end()) { - _insts->put(type, body); - } - else { - it->second->dec(); - it->second = body; - } - body->inc(); - } - - u64 FunctionValue::arity() const { - return _builtin ? _builtin_arity : _args.size(); - } - - const Value& FunctionValue::body() const { - return _code; - } - - AliasValue::AliasValue(const Value& value): - _value(value) {} - - Value& AliasValue::value() { - return _value; - } - - const Value& AliasValue::value() const { - return _value; - } - - MacroValue::MacroValue(ref env, const vector& args, - const Value& code): - _code(code), _builtin(nullptr), _env(env), _args(args) {} - - MacroValue::MacroValue(ref env, BuiltinMacro builtin, - u64 arity): - _builtin(builtin), _env(env), _builtin_arity(arity) {} - - const vector& MacroValue::args() const { - return _args; - } - - bool MacroValue::is_builtin() const { - return _builtin; - } - - BuiltinMacro MacroValue::get_builtin() const { - return _builtin; - } - - ref MacroValue::get_env() { - return _env; - } - - const ref MacroValue::get_env() const { - return _env; - } - - u64 MacroValue::arity() const { - return _builtin ? _builtin_arity : _args.size(); - } - - const Value& MacroValue::body() const { - return _code; - } - - vector to_vector(const Value& list) { - vector values; - const Value* v = &list; - while (v->is_list()) { - values.push(v->get_list().head()); - v = &v->get_list().tail(); - } - return values; - } - - Value lower(const Value& v) { - if (v.is_runtime()) return v; - else if (v.is_void()) return new ASTVoid(v.loc()); - else if (v.is_int()) - return new ASTInt(v.loc(), v.get_int()); - else if (v.is_symbol()) - return new ASTSymbol(v.loc(), v.get_symbol()); - else if (v.is_string()) - return new ASTString(v.loc(), v.get_string()); - else if (v.is_bool()) - return new ASTBool(v.loc(), v.get_bool()); - else if (v.is_list()) { - vector vals = to_vector(v); - ASTNode* acc = new ASTVoid(v.loc()); - for (i64 i = vals.size() - 1; i >= 0; i --) { - Value l = lower(vals[i]); - acc = new ASTCons(v.loc(), l.get_runtime(), acc); - } - return acc; - } - else if (v.is_error()) - return new ASTSingleton(ERROR); - else { - err(v.loc(), "Couldn't lower value '", v, "'."); - return error(); - } - } - - Value binary_arithmetic(const Value& lhs, const Value& rhs, - i64(*op)(i64, i64)) { - if (!lhs.is_int() && !lhs.is_error()) { - err(lhs.loc(), "Expected integer value in arithmetic expression, found '", - lhs.type(), "'."); - return error(); - } - if (!rhs.is_int() && !rhs.is_error()) { - err(rhs.loc(), "Expected integer value in arithmetic expression, found '", - rhs.type(), "'."); - return error(); - } - if (lhs.is_error() || rhs.is_error()) return error(); - - return Value(op(lhs.get_int(), rhs.get_int())); - } - - bool is_runtime_binary(const Value& lhs, const Value& rhs) { - return lhs.is_runtime() || rhs.is_runtime(); - } - - Value lower(ASTMathOp op, const Value& lhs, const Value& rhs) { - return new ASTBinaryMath(lhs.loc(), op, lower(lhs).get_runtime(), - lower(rhs).get_runtime()); - } - - Value add(const Value& lhs, const Value& rhs) { - if (is_runtime_binary(lhs, rhs)) return lower(AST_ADD, lhs, rhs); - return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a + b; }); - } - - Value sub(const Value& lhs, const Value& rhs) { - if (is_runtime_binary(lhs, rhs)) return lower(AST_SUB, lhs, rhs); - return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a - b; }); - } - - Value mul(const Value& lhs, const Value& rhs) { - if (is_runtime_binary(lhs, rhs)) return lower(AST_MUL, lhs, rhs); - return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a * b; }); - } - - Value div(const Value& lhs, const Value& rhs) { - if (is_runtime_binary(lhs, rhs)) return lower(AST_DIV, lhs, rhs); - return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a / b; }); - } - - Value rem(const Value& lhs, const Value& rhs) { - if (is_runtime_binary(lhs, rhs)) return lower(AST_REM, lhs, rhs); - return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a % b; }); - } - - Value binary_logic(const Value& lhs, const Value& rhs, bool(*op)(bool, bool)) { - if (!lhs.is_bool() && !lhs.is_error()) { - err(lhs.loc(), "Expected boolean value in logical expression, found '", - lhs.type(), "'."); - return error(); - } - if (!rhs.is_bool() && !rhs.is_error()) { - err(rhs.loc(), "Expected boolean value in logical expression, found '", - rhs.type(), "'."); - return error(); - } - if (lhs.is_error() || rhs.is_error()) return error(); - - return Value(op(lhs.get_bool(), rhs.get_bool()), BOOL); - } - - Value lower(ASTLogicOp op, const Value& lhs, const Value& rhs) { - return new ASTBinaryLogic(lhs.loc(), op, lower(lhs).get_runtime(), - lower(rhs).get_runtime()); - } - - Value logical_and(const Value& lhs, const Value& rhs) { - if (is_runtime_binary(lhs, rhs)) return lower(AST_AND, lhs, rhs); - return binary_logic(lhs, rhs, [](bool a, bool b) -> bool { return a && b; }); - } - - Value logical_or(const Value& lhs, const Value& rhs) { - if (is_runtime_binary(lhs, rhs)) return lower(AST_OR, lhs, rhs); - return binary_logic(lhs, rhs, [](bool a, bool b) -> bool { return a || b; }); - } - - Value logical_xor(const Value& lhs, const Value& rhs) { - if (is_runtime_binary(lhs, rhs)) return lower(AST_XOR, lhs, rhs); - return binary_logic(lhs, rhs, [](bool a, bool b) -> bool { return a ^ b; }); - } - - Value logical_not(const Value& v) { - if (v.is_runtime()) return new ASTNot(v.loc(), lower(v).get_runtime()); - - if (!v.is_bool() && !v.is_error()) { - err(v.loc(), "Expected boolean value in logical expression, found '", - v.type(), "'."); - return error(); - } - if (v.is_error()) return error(); - - return Value(!v.get_bool(), BOOL); - } - - Value lower(ASTEqualOp op, const Value& lhs, const Value& rhs) { - return new ASTBinaryEqual(lhs.loc(), op, lower(lhs).get_runtime(), - lower(rhs).get_runtime()); - } - - Value equal(const Value& lhs, const Value& rhs) { - if (lhs.is_error() || rhs.is_error()) return error(); - if (is_runtime_binary(lhs, rhs)) return lower(AST_EQUAL, lhs, rhs); - return Value(lhs == rhs, BOOL); - } - - Value inequal(const Value& lhs, const Value& rhs) { - if (lhs.is_error() || rhs.is_error()) return error(); - if (is_runtime_binary(lhs, rhs)) return lower(AST_INEQUAL, lhs, rhs); - return Value(!equal(lhs, rhs).get_bool(), BOOL); - } - - Value binary_relation(const Value& lhs, const Value& rhs, bool(*int_op)(i64, i64), bool(*string_op)(string, string)) { - if (!lhs.is_int() && !lhs.is_string() && !lhs.is_error()) { - err(lhs.loc(), "Expected integer or string value in relational expression, found '", - lhs.type(), "'."); - return error(); - } - if (!rhs.is_int() && !rhs.is_string() && !rhs.is_error()) { - err(rhs.loc(), "Expected integer or string value in relational expression, found '", - rhs.type(), "'."); - return error(); - } - if ((lhs.is_int() && !rhs.is_int()) || (lhs.is_string() && !rhs.is_string())) { - err(rhs.loc(), "Invalid parameters to relational expression: '", lhs.type(), "' and '", rhs.type(), "'."); - return error(); - } - if (lhs.is_error() || rhs.is_error()) return error(); - - if (lhs.is_string()) return Value(string_op(lhs.get_string(), rhs.get_string()), BOOL); - return Value(int_op(lhs.get_int(), rhs.get_int()), BOOL); - } - - Value lower(ASTRelOp op, const Value& lhs, const Value& rhs) { - return new ASTBinaryRel(lhs.loc(), op, lower(lhs).get_runtime(), - lower(rhs).get_runtime()); - } - - Value less(const Value& lhs, const Value& rhs) { - if (is_runtime_binary(lhs, rhs)) return lower(AST_LESS, lhs, rhs); - return binary_relation(lhs, rhs, [](i64 a, i64 b) -> bool { return a < b; }, [](string a, string b) -> bool { return a < b; }); - } - - Value greater(const Value& lhs, const Value& rhs) { - if (is_runtime_binary(lhs, rhs)) return lower(AST_GREATER, lhs, rhs); - return binary_relation(lhs, rhs, [](i64 a, i64 b) -> bool { return a > b; }, [](string a, string b) -> bool { return a > b; }); - } - - Value less_equal(const Value& lhs, const Value& rhs) { - if (is_runtime_binary(lhs, rhs)) return lower(AST_LESS_EQUAL, lhs, rhs); - return binary_relation(lhs, rhs, [](i64 a, i64 b) -> bool { return a <= b; }, [](string a, string b) -> bool { return a <= b; }); - } - - Value greater_equal(const Value& lhs, const Value& rhs) { - if (is_runtime_binary(lhs, rhs)) return lower(AST_GREATER_EQUAL, lhs, rhs); - return binary_relation(lhs, rhs, [](i64 a, i64 b) -> bool { return a >= b; }, [](string a, string b) -> bool { return a >= b; }); - } - - Value head(const Value& v) { - if (v.is_runtime()) return new ASTHead(v.loc(), lower(v).get_runtime()); - if (!v.is_list() && !v.is_error()) { - err(v.loc(), "Can only get head of value of list type, given '", - v.type(), "'."); - return error(); - } - if (v.is_error()) return error(); - - return v.get_list().head(); - } - - Value tail(const Value& v) { - if (v.is_runtime()) return new ASTTail(v.loc(), lower(v).get_runtime()); - if (!v.is_list() && !v.is_error()) { - err(v.loc(), "Can only get tail of value of list type, given '", - v.type(), "'."); - return error(); - } - if (v.is_error()) return error(); - - return v.get_list().tail(); - } - - Value cons(const Value& head, const Value& tail) { - if (head.is_runtime() || tail.is_runtime()) { - return new ASTCons(head.loc(), lower(head).get_runtime(), - lower(tail).get_runtime()); - } - if (!tail.is_list() && !tail.is_void() && !tail.is_error()) { - err(tail.loc(), "Tail of cons cell must be a list or void, given '", - tail.type(), "'."); - return error(); - } - if (head.is_error() || tail.is_error()) return error(); - - return Value(new ListValue(head, tail)); - } - - Value empty() { - return Value(VOID); - } - - Value list_of(const Value& element) { - if (element.is_error()) return error(); - return cons(element, empty()); - } - - Value list_of(const vector& elements) { - Value l = empty(); - for (i64 i = i64(elements.size()) - 1; i >= 0; i --) { - l = cons(elements[i], l); - } - return l; - } - - Value is_empty(const Value& list) { - if (list.is_runtime()) - return new ASTIsEmpty(list.loc(), lower(list).get_runtime()); - if (!list.is_list() && !list.is_void() && !list.is_error()) { - err(list.loc(), "Can only get tail of value of list type, given '", - list.type(), "'."); - return error(); - } - return Value(list.is_void(), BOOL); - } - - Value error() { - return Value(ERROR); - } - - Value length(const Value& val) { - if (val.is_error()) return error(); - - if (val.is_runtime()) - return new ASTLength(val.loc(), lower(val).get_runtime()); - - if (val.is_string()) return Value(i64(val.get_string().size())); - else if (val.is_list()) return Value(i64(to_vector(val).size())); - else if (val.is_product()) return Value(i64(val.get_product().size())); - else if (val.is_array()) return Value(i64(val.get_array().size())); - else { - err(val.loc(), "Cannot get length of value of type '", val.type(), "'."); - return error(); - } - } - - Value tuple_of(const vector& elements) { - for (const Value& v : elements) { - if (v.is_runtime()) { - err(v.loc(), "Cannot compile tuples yet."); - return error(); - } + return type() == other.type(); } - return Value(new ProductValue(elements)); - } - Value array_of(const vector& elements) { - for (const Value& v : elements) { - if (v.is_runtime()) { - err(v.loc(), "Cannot compile arrays yet."); - return error(); - } - } - return Value(new ArrayValue(elements)); - } - - Value at(const Value& val, const Value& idx) { - if (val.is_error() || idx.is_error()) return error(); - if (val.is_runtime() || idx.is_runtime()) { - vector args; - Value s = lower(val), i = lower(idx); - args.push(s.get_runtime()); - args.push(i.get_runtime()); - vector arg_types; - arg_types.push(args[0]->type()); - arg_types.push(args[1]->type()); - if (args[0]->type() == STRING) { - return new ASTNativeCall(val.loc(), "_char_at", INT, args, arg_types); - } - else { - err(val.loc(), "Accesses not implemented in AST yet."); - return error(); - } - } - if (!idx.is_int()) { - err(idx.loc(), "Expected integer index in accessor, given '", val.type(), "'."); - return error(); - } - if (val.is_string()) return Value(i64(val.get_string()[idx.get_int()])); - else if (val.is_product()) return val.get_product()[idx.get_int()]; - else if (val.is_array()) return val.get_array()[idx.get_int()]; - else { - err(val.loc(), "Cannot index into value of type '", val.type(), "'."); - return error(); - } - } - - Value strcat(const Value& a, const Value &b) { - if (a.is_error() || b.is_error()) return error(); - if (a.is_runtime() || b.is_runtime()) { - vector args; - Value al = lower(a), bl = lower(b); - args.push(al.get_runtime()); - args.push(bl.get_runtime()); - vector arg_types; - arg_types.push(STRING); - arg_types.push(STRING); - return new ASTNativeCall(a.loc(), "_strcat", STRING, args, arg_types); - } - if (!a.is_string() || !b.is_string()) { - err(a.loc(), "Expected string and string, given '", a.type(), "' and '", b.type(), "'."); - return error(); - } - return Value(a.get_string() + b.get_string(), STRING); - } - - Value substr(const Value& str, const Value &start, const Value &end) { - if (str.is_error() || start.is_error() || end.is_error()) return error(); - if (str.is_runtime() || start.is_runtime() || end.is_runtime()) { - vector args; - Value strl = lower(str), startl = lower(start), endl = lower(end); - args.push(strl.get_runtime()); - args.push(startl.get_runtime()); - args.push(endl.get_runtime()); - vector arg_types; - arg_types.push(STRING); - arg_types.push(INT); - arg_types.push(INT); - return new ASTNativeCall(str.loc(), "_substr", STRING, args, arg_types); - } - if (!str.is_string() || !start.is_int() || !end.is_int()) { - err(str.loc(), "Expected string, integer, and integer, given '", str.type(), "' and '", start.type(), "' and '", end.type(), "'."); - return error(); - } - - return end.get_int() < start.get_int() - ? Value("", STRING) - : Value(string(str.get_string()[{start.get_int(), end.get_int()}]), STRING); - } - - Value type_of(const Value& v) { - return Value(v.type(), TYPE); - } - - Value cast(const Value& val, const Type* type) { - if (val.is_product()) { - if (type == TYPE) { - vector ts; - for (const Value& v : val.get_product()) - ts.push(v.get_type()); - return Value(find(ts), TYPE); - } + Value Value::clone() const { + if (is_list()) return Value(new ListValue(get_list().head().clone(), get_list().tail().clone())); + else if (is_string()) + return Value(get_string(), STRING); + else if (is_sum()) + return Value(new SumValue(get_sum().value()), type()); + else if (is_intersect()) { + map values; + for (const auto& p : get_intersect()) values.put(p.first, p.second.clone()); + return Value(new IntersectValue(values), type()); + } else if (is_product()) { + vector values; + for (const Value& v : get_product()) values.push(v.clone()); + return Value(new ProductValue(values)); + } else if (is_array()) { + vector values; + for (const Value& v : get_array()) values.push(v.clone()); + return Value(new ProductValue(values)); + } else if (is_function()) { + if (get_function().is_builtin()) { + return Value(new FunctionValue(get_function().get_env()->clone(), get_function().get_builtin(), + get_function().name()), + type()); + } else { + return Value(new FunctionValue(get_function().get_env()->clone(), get_function().args(), + get_function().body().clone()), + type()); + } + } else if (is_alias()) + return Value(new AliasValue(get_alias().value())); + else if (is_macro()) { + if (get_macro().is_builtin()) { + return Value(new MacroValue(get_macro().get_env()->clone(), get_macro().get_builtin())); + } else { + return Value( + new MacroValue(get_macro().get_env()->clone(), get_macro().args(), get_macro().body().clone())); + } + } else if (is_runtime()) { + // todo: ast cloning + } + return *this; } - if (val.is_list() && type == TYPE) { - if (length(val) != 1) { - err(val.loc(), "Only single-element lists can be treated as types."); - return error(); - } - return find(head(val).get_type()); - } - - if (val.is_sum() && val.get_sum().value().type() == type) - return val.get_sum().value(); - else { - err(val.loc(), "Sum value does not currently contain value of type '", type, "'."); - return error(); - } - - if (type->kind() == KIND_SUM) { - return Value(new SumValue(val), val.type()); - } - - err(val.loc(), "Could not convert value to type '", type, "'."); - } - - Value is(const Value& val, const Value& type) { - if (!type.is_type()) { - err(type.loc(), "Expected type value in is-expression, given '", type.type(), "'."); - return error(); - } - if (val.type() == type.get_type()) - return Value(true, BOOL); - else if (val.is_sum() && val.get_sum().value().type() == type.get_type()) - return Value(true, BOOL); - else return Value(false, BOOL); - } - - Value as(const Value& val, const Value& type) { - if (!type.is_type()) { - err(type.loc(), "Expected type value in explicit cast, given '", type.type(), "'."); - return error(); - } - // if (val.is_runtime()) { - // return Value(new ASTAnnotate(val.loc(), val.get_runtime(), type.get_type())); - // } - // else if (val.type()->coerces_to(*type.get_type())) { - // err(val.loc(), "Could not unify value of type '", val.type(), "' with type '", - // type.get_type(), "'."); - // return error(); - // } - return cast(val, type.get_type()); - } - - Value annotate(const Value& val, const Value& type) { - if (!type.is_type()) { - err(type.loc(), "Expected type value in annotation, given '", type.type(), "'."); - return error(); - } - if (val.is_runtime()) { - return Value(new ASTAnnotate(val.loc(), val.get_runtime(), type.get_type())); - } - else if (val.type()->coerces_to(*type.get_type())) { - err(val.loc(), "Could not unify value of type '", val.type(), "' with type '", - type.get_type(), "'."); - return error(); - } - return cast(val, type.get_type()); - } - - ASTNode* instantiate(SourceLocation loc, FunctionValue& fn, - const Type* args_type) { - ref new_env = fn.get_env()->clone(); - new_env->make_runtime(); - u32 j = 0; - vector new_args; - for (u32 i = 0; i < fn.arity(); i ++) { - if (!(fn.args()[i] & KEYWORD_ARG_BIT)) { - auto it = new_env->find(symbol_for(fn.args()[i] & ARG_NAME_MASK)); - const Type* argt = ((const ProductType*)args_type)->member(j); - it->value = new ASTSingleton(argt); - j ++; - new_args.push(fn.args()[i]); - } - } - Value cloned = fn.body().clone(); - prep(new_env, cloned); - Value v = eval(new_env, cloned); - if (v.is_error()) return nullptr; - if (!v.is_runtime()) v = lower(v); - ASTNode* result = new ASTFunction(loc, new_env, args_type, - new_args, v.get_runtime(), fn.name()); - fn.instantiate(args_type, result); - return result; - } - - void find_calls(FunctionValue& fn, ref env, const Value& term, - set& visited) { - if (!term.is_list()) return; - Value h = head(term); - if (h.is_symbol()) { - Def* def = env->find(symbol_for(h.get_symbol())); - if (def && def->value.is_function()) { - FunctionValue* f = &def->value.get_function(); - if (visited.find(f) == visited.end()) { - visited.insert(f); - if (f != &fn) find_calls(*f, f->get_env(), f->body(), visited); - fn.add_call(f); - } - } - } - if (!introduces_env(term)) { - const Value* v = &term; - while (v->is_list()) { - find_calls(fn, env, v->get_list().head(), visited); - v = &v->get_list().tail(); - } - } - } - - Value call(ref env, Value& function, const Value& arg) { - if (function.is_runtime()) { - u32 argc = arg.get_product().size(); - vector argts; - vector lowered_args; - for (u32 i = 0; i < argc; i ++) { - if (arg.get_product()[i].is_function()) { - vector inner_argts; - for (u32 j = 0; j < arg.get_product()[i].get_function().arity(); j ++) - inner_argts.push(find()); - argts.push(find( - find(inner_argts), find())); - lowered_args.push(arg.get_product()[i]); // we'll lower this later - } - else { - Value lowered = lower(arg.get_product()[i]); - argts.push(((const RuntimeType*)lowered.type())->base()); - lowered_args.push(lowered); - } - } - const Type* argt = find(argts); - vector arg_nodes; - for (u32 i = 0; i < lowered_args.size(); i ++) { - if (lowered_args[i].is_function()) { - const Type* t = ((const ProductType*)argt)->member(i); - if (!t->concrete() || t->kind() != KIND_FUNCTION) { - err(lowered_args[i].loc(), "Could not deduce type for function ", - "parameter, resolved to '", t, "'."); - return error(); - } - const Type* fnarg = ((const FunctionType*)t)->arg(); - FunctionValue& fn = lowered_args[i].get_function(); - ASTNode* argbody = fn.instantiation(fnarg); - if (!argbody) { - fn.instantiate(fnarg, new ASTIncompleteFn(lowered_args[i].loc(), - fnarg, fn.name())); - argbody = instantiate(lowered_args[i].loc(), fn, fnarg); - } - if (!argbody) return error(); - arg_nodes.push(argbody); - } - else arg_nodes.push(lowered_args[i].get_runtime()); - } - return new ASTCall(function.loc(), function.get_runtime(), arg_nodes); - } - - if (!function.is_function() && !function.is_error()) { - err(function.loc(), "Called value is not a procedure."); - return error(); - } - if (!arg.is_product() && !arg.is_error()) { - err(arg.loc(), "Arguments not provided as a product."); - return error(); - } - if (function.is_error() || arg.is_error()) return error(); - - FunctionValue& fn = function.get_function(); - if (fn.is_builtin()) { - return fn.get_builtin()(env, arg); - } - else { - ref env = fn.get_env(); - u32 argc = arg.get_product().size(), arity = fn.args().size(); - if (argc != arity) { - err(function.loc(), "Procedure requires ", arity, " arguments, ", - argc, " provided."); + bool Value::operator!=(const Value& other) const { + return !(*this == other); + } + + void Value::set_location(SourceLocation loc) { + _loc = loc; + } + + SourceLocation Value::loc() const { + return _loc; + } + + StringValue::StringValue(const string& value) : _value(value) {} + + string& StringValue::value() { + return _value; + } + + const string& StringValue::value() const { + return _value; + } + + ListValue::ListValue(const Value& head, const Value& tail) : _head(head), _tail(tail) {} + + Value& ListValue::head() { + return _head; + } + + const Value& ListValue::head() const { + return _head; + } + + Value& ListValue::tail() { + return _tail; + } + + const Value& ListValue::tail() const { + return _tail; + } + + SumValue::SumValue(const Value& value) : _value(value) {} + + Value& SumValue::value() { + return _value; + } + + const Value& SumValue::value() const { + return _value; + } + + IntersectValue::IntersectValue(const map& values) : _values(values) {} + + u32 IntersectValue::size() const { + return _values.size(); + } + + bool IntersectValue::has(const Type* t) const { + return _values.find(t) != _values.end(); + } + + map::const_iterator IntersectValue::begin() const { + return _values.begin(); + } + + map::const_iterator IntersectValue::end() const { + return _values.end(); + } + + map::iterator IntersectValue::begin() { + return _values.begin(); + } + + map::iterator IntersectValue::end() { + return _values.end(); + } + + const map& IntersectValue::values() const { + return _values; + } + + map& IntersectValue::values() { + return _values; + } + + ProductValue::ProductValue(const vector& values) : _values(values) {} + + u32 ProductValue::size() const { + return _values.size(); + } + + Value& ProductValue::operator[](u32 i) { + return _values[i]; + } + + const Value& ProductValue::operator[](u32 i) const { + return _values[i]; + } + + const Value* ProductValue::begin() const { + return _values.begin(); + } + + const Value* ProductValue::end() const { + return _values.end(); + } + + Value* ProductValue::begin() { + return _values.begin(); + } + + Value* ProductValue::end() { + return _values.end(); + } + + const vector& ProductValue::values() const { + return _values; + } + + ArrayValue::ArrayValue(const vector& values) : ProductValue(values) {} + + const u64 KEYWORD_ARG_BIT = 1ul << 63; + const u64 ARG_NAME_MASK = ~KEYWORD_ARG_BIT; + + FunctionValue::FunctionValue(ref env, const vector& args, const Value& code, i64 name) + : _name(name), _code(code), _builtin(nullptr), _env(env), _args(args), _insts(nullptr), _calls(nullptr) {} + + FunctionValue::FunctionValue(ref env, const Builtin& builtin, i64 name) + : _name(name), _builtin(&builtin), _env(env), _insts(nullptr), _calls(nullptr) {} + + FunctionValue::~FunctionValue() { + if (_insts) { + for (auto& p : *_insts) p.second->dec(); + delete _insts; + } + if (_calls) { + for (auto& p : *_calls) ((FunctionValue*)p)->dec(); + delete _calls; + } + } + + FunctionValue::FunctionValue(const FunctionValue& other) + : _name(other._name), _code(other._code.clone()), _builtin(other._builtin), _env(other._env), + _args(other._args), _insts(other._insts ? new map(*other._insts) : nullptr), + _calls(other._calls ? new set(*other._calls) : nullptr) { + if (_insts) + for (auto& p : *_insts) p.second->inc(); + if (_calls) + for (auto p : *_calls) ((FunctionValue*)p)->inc(); + } + + FunctionValue& FunctionValue::operator=(const FunctionValue& other) { + if (this != &other) { + if (_insts) { + for (auto& p : *_insts) p.second->dec(); + delete _insts; + } + if (_calls) { + for (auto& p : *_calls) ((FunctionValue*)p)->dec(); + delete _calls; + } + _name = other._name; + _code = other._code.clone(); + _builtin = other._builtin; + _env = other._env; + _args = other._args; + _insts = other._insts ? new map(*other._insts) : nullptr; + _calls = other._calls ? new set(*other._calls) : nullptr; + if (_insts) + for (auto& p : *_insts) p.second->inc(); + if (_calls) + for (auto& p : *_calls) ((FunctionValue*)p)->inc(); + } + return *this; + } + + const vector& FunctionValue::args() const { + return _args; + } + + bool FunctionValue::is_builtin() const { + return _builtin; + } + + const Builtin& FunctionValue::get_builtin() const { + return *_builtin; + } + + ref FunctionValue::get_env() { + return _env; + } + + const ref FunctionValue::get_env() const { + return _env; + } + + i64 FunctionValue::name() const { + return _name; + } + + bool FunctionValue::found_calls() const { + return _calls; + } + + bool FunctionValue::recursive() const { + return _calls && _calls->find(this) != _calls->end(); + } + + void FunctionValue::add_call(const FunctionValue* other) { + if (!_calls) _calls = new set(); + if (other != this && other->_calls) + for (const FunctionValue* f : *other->_calls) { + _calls->insert(f); + ((FunctionValue*)f)->inc(); // evil + } + _calls->insert(other); + ((FunctionValue*)other)->inc(); + } + + ASTNode* FunctionValue::instantiation(const Type* type) const { + if (_insts) { + auto it = _insts->find(type); + if (it != _insts->end()) return it->second; + } + return nullptr; + } + + void FunctionValue::instantiate(const Type* type, ASTNode* body) { + if (!_insts) _insts = new map(); + auto it = _insts->find(type); + if (it == _insts->end()) { + _insts->put(type, body); + } else { + it->second->dec(); + it->second = body; + } + body->inc(); + } + + u64 FunctionValue::arity() const { + return _builtin ? ((const FunctionType*)_builtin->type())->arity() : _args.size(); + } + + const Value& FunctionValue::body() const { + return _code; + } + + AliasValue::AliasValue(const Value& value) : _value(value) {} + + Value& AliasValue::value() { + return _value; + } + + const Value& AliasValue::value() const { + return _value; + } + + MacroValue::MacroValue(ref env, const vector& args, const Value& code) + : _code(code), _builtin(nullptr), _env(env), _args(args) {} + + MacroValue::MacroValue(ref env, const Builtin& builtin) : _builtin(&builtin), _env(env) {} + + const vector& MacroValue::args() const { + return _args; + } + + bool MacroValue::is_builtin() const { + return _builtin; + } + + const Builtin& MacroValue::get_builtin() const { + return *_builtin; + } + + ref MacroValue::get_env() { + return _env; + } + + const ref MacroValue::get_env() const { + return _env; + } + + u64 MacroValue::arity() const { + return _builtin ? ((const MacroType*)_builtin->type())->arity() : _args.size(); + } + + const Value& MacroValue::body() const { + return _code; + } + + vector to_vector(const Value& list) { + vector values; + const Value* v = &list; + while (v->is_list()) { + values.push(v->get_list().head()); + v = &v->get_list().tail(); + } + return values; + } + + Value lower(const Value& v) { + if (v.is_runtime()) return v; + else if (v.is_void()) + return new ASTVoid(v.loc()); + else if (v.is_int()) + return new ASTInt(v.loc(), v.get_int()); + else if (v.is_symbol()) + return new ASTSymbol(v.loc(), v.get_symbol()); + else if (v.is_string()) + return new ASTString(v.loc(), v.get_string()); + else if (v.is_bool()) + return new ASTBool(v.loc(), v.get_bool()); + else if (v.is_list()) { + vector vals = to_vector(v); + ASTNode* acc = new ASTVoid(v.loc()); + for (i64 i = vals.size() - 1; i >= 0; i--) { + Value l = lower(vals[i]); + acc = new ASTCons(v.loc(), l.get_runtime(), acc); + } + return acc; + } else if (v.is_error()) + return new ASTSingleton(ERROR); + else { + err(v.loc(), "Couldn't lower value '", v, "'."); + return error(); + } + } + + Value binary_arithmetic(const Value& lhs, const Value& rhs, i64 (*op)(i64, i64)) { + if (!lhs.is_int() && !lhs.is_error()) { + err(lhs.loc(), "Expected integer value in arithmetic expression, found '", lhs.type(), "'."); + return error(); + } + if (!rhs.is_int() && !rhs.is_error()) { + err(rhs.loc(), "Expected integer value in arithmetic expression, found '", rhs.type(), "'."); + return error(); + } + if (lhs.is_error() || rhs.is_error()) return error(); + + return Value(op(lhs.get_int(), rhs.get_int())); + } + + bool is_runtime_binary(const Value& lhs, const Value& rhs) { + return lhs.is_runtime() || rhs.is_runtime(); + } + + Value lower(ASTMathOp op, const Value& lhs, const Value& rhs) { + return new ASTBinaryMath(lhs.loc(), op, lower(lhs).get_runtime(), lower(rhs).get_runtime()); + } + + Value add(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_ADD, lhs, rhs); + return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a + b; }); + } + + Value sub(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_SUB, lhs, rhs); + return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a - b; }); + } + + Value mul(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_MUL, lhs, rhs); + return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a * b; }); + } + + Value div(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_DIV, lhs, rhs); + return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a / b; }); + } + + Value rem(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_REM, lhs, rhs); + return binary_arithmetic(lhs, rhs, [](i64 a, i64 b) -> i64 { return a % b; }); + } + + Value binary_logic(const Value& lhs, const Value& rhs, bool (*op)(bool, bool)) { + if (!lhs.is_bool() && !lhs.is_error()) { + err(lhs.loc(), "Expected boolean value in logical expression, found '", lhs.type(), "'."); + return error(); + } + if (!rhs.is_bool() && !rhs.is_error()) { + err(rhs.loc(), "Expected boolean value in logical expression, found '", rhs.type(), "'."); + return error(); + } + if (lhs.is_error() || rhs.is_error()) return error(); + + return Value(op(lhs.get_bool(), rhs.get_bool()), BOOL); + } + + Value lower(ASTLogicOp op, const Value& lhs, const Value& rhs) { + return new ASTBinaryLogic(lhs.loc(), op, lower(lhs).get_runtime(), lower(rhs).get_runtime()); + } + + Value logical_and(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_AND, lhs, rhs); + return binary_logic(lhs, rhs, [](bool a, bool b) -> bool { return a && b; }); + } + + Value logical_or(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_OR, lhs, rhs); + return binary_logic(lhs, rhs, [](bool a, bool b) -> bool { return a || b; }); + } + + Value logical_xor(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_XOR, lhs, rhs); + return binary_logic(lhs, rhs, [](bool a, bool b) -> bool { return a ^ b; }); + } + + Value logical_not(const Value& v) { + if (v.is_runtime()) return new ASTNot(v.loc(), lower(v).get_runtime()); + + if (!v.is_bool() && !v.is_error()) { + err(v.loc(), "Expected boolean value in logical expression, found '", v.type(), "'."); + return error(); + } + if (v.is_error()) return error(); + + return Value(!v.get_bool(), BOOL); + } + + Value lower(ASTEqualOp op, const Value& lhs, const Value& rhs) { + return new ASTBinaryEqual(lhs.loc(), op, lower(lhs).get_runtime(), lower(rhs).get_runtime()); + } + + Value equal(const Value& lhs, const Value& rhs) { + if (lhs.is_error() || rhs.is_error()) return error(); + if (is_runtime_binary(lhs, rhs)) return lower(AST_EQUAL, lhs, rhs); + return Value(lhs == rhs, BOOL); + } + + Value inequal(const Value& lhs, const Value& rhs) { + if (lhs.is_error() || rhs.is_error()) return error(); + if (is_runtime_binary(lhs, rhs)) return lower(AST_INEQUAL, lhs, rhs); + return Value(!equal(lhs, rhs).get_bool(), BOOL); + } + + Value binary_relation(const Value& lhs, const Value& rhs, bool (*int_op)(i64, i64), + bool (*string_op)(string, string)) { + if (!lhs.is_int() && !lhs.is_string() && !lhs.is_error()) { + err(lhs.loc(), "Expected integer or string value in relational expression, found '", lhs.type(), "'."); + return error(); + } + if (!rhs.is_int() && !rhs.is_string() && !rhs.is_error()) { + err(rhs.loc(), "Expected integer or string value in relational expression, found '", rhs.type(), "'."); + return error(); + } + if ((lhs.is_int() && !rhs.is_int()) || (lhs.is_string() && !rhs.is_string())) { + err(rhs.loc(), "Invalid parameters to relational expression: '", lhs.type(), "' and '", rhs.type(), "'."); + return error(); + } + if (lhs.is_error() || rhs.is_error()) return error(); + + if (lhs.is_string()) return Value(string_op(lhs.get_string(), rhs.get_string()), BOOL); + return Value(int_op(lhs.get_int(), rhs.get_int()), BOOL); + } + + Value lower(ASTRelOp op, const Value& lhs, const Value& rhs) { + return new ASTBinaryRel(lhs.loc(), op, lower(lhs).get_runtime(), lower(rhs).get_runtime()); + } + + Value less(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_LESS, lhs, rhs); + return binary_relation( + lhs, rhs, [](i64 a, i64 b) -> bool { return a < b; }, [](string a, string b) -> bool { return a < b; }); + } + + Value greater(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_GREATER, lhs, rhs); + return binary_relation( + lhs, rhs, [](i64 a, i64 b) -> bool { return a > b; }, [](string a, string b) -> bool { return a > b; }); + } + + Value less_equal(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_LESS_EQUAL, lhs, rhs); + return binary_relation( + lhs, rhs, [](i64 a, i64 b) -> bool { return a <= b; }, [](string a, string b) -> bool { return a <= b; }); + } + + Value greater_equal(const Value& lhs, const Value& rhs) { + if (is_runtime_binary(lhs, rhs)) return lower(AST_GREATER_EQUAL, lhs, rhs); + return binary_relation( + lhs, rhs, [](i64 a, i64 b) -> bool { return a >= b; }, [](string a, string b) -> bool { return a >= b; }); + } + + Value head(const Value& v) { + if (v.is_runtime()) return new ASTHead(v.loc(), lower(v).get_runtime()); + if (!v.is_list() && !v.is_error()) { + err(v.loc(), "Can only get head of value of list type, given '", v.type(), "'."); + return error(); + } + if (v.is_error()) return error(); + + return v.get_list().head(); + } + + Value tail(const Value& v) { + if (v.is_runtime()) return new ASTTail(v.loc(), lower(v).get_runtime()); + if (!v.is_list() && !v.is_error()) { + err(v.loc(), "Can only get tail of value of list type, given '", v.type(), "'."); + return error(); + } + if (v.is_error()) return error(); + + return v.get_list().tail(); + } + + Value cons(const Value& head, const Value& tail) { + if (head.is_runtime() || tail.is_runtime()) { + return new ASTCons(head.loc(), lower(head).get_runtime(), lower(tail).get_runtime()); + } + if (!tail.is_list() && !tail.is_void() && !tail.is_error()) { + err(tail.loc(), "Tail of cons cell must be a list or void, given '", tail.type(), "'."); + return error(); + } + if (head.is_error() || tail.is_error()) return error(); + + return Value(new ListValue(head, tail)); + } + + Value empty() { + return Value(VOID); + } + + Value list_of(const Value& element) { + if (element.is_error()) return error(); + return cons(element, empty()); + } + + Value list_of(const vector& elements) { + Value l = empty(); + for (i64 i = i64(elements.size()) - 1; i >= 0; i--) { l = cons(elements[i], l); } + return l; + } + + Value is_empty(const Value& list) { + if (list.is_runtime()) return new ASTIsEmpty(list.loc(), lower(list).get_runtime()); + if (!list.is_list() && !list.is_void() && !list.is_error()) { + err(list.loc(), "Can only get tail of value of list type, given '", list.type(), "'."); + return error(); + } + return Value(list.is_void(), BOOL); + } + + Value error() { + return Value(ERROR); + } + + Value length(const Value& val) { + if (val.is_error()) return error(); + + if (val.is_runtime()) return new ASTLength(val.loc(), lower(val).get_runtime()); + + if (val.is_string()) return Value(i64(val.get_string().size())); + else if (val.is_list()) + return Value(i64(to_vector(val).size())); + else if (val.is_product()) + return Value(i64(val.get_product().size())); + else if (val.is_array()) + return Value(i64(val.get_array().size())); + else { + err(val.loc(), "Cannot get length of value of type '", val.type(), "'."); + return error(); + } + } + + Value tuple_of(const vector& elements) { + for (const Value& v : elements) { + if (v.is_runtime()) { + err(v.loc(), "Cannot compile tuples yet."); + return error(); + } + } + return Value(new ProductValue(elements)); + } + + Value array_of(const vector& elements) { + for (const Value& v : elements) { + if (v.is_runtime()) { + err(v.loc(), "Cannot compile arrays yet."); + return error(); + } + } + return Value(new ArrayValue(elements)); + } + + Value at(const Value& val, const Value& idx) { + if (val.is_error() || idx.is_error()) return error(); + if (val.is_runtime() || idx.is_runtime()) { + vector args; + Value s = lower(val), i = lower(idx); + args.push(s.get_runtime()); + args.push(i.get_runtime()); + vector arg_types; + arg_types.push(args[0]->type()); + arg_types.push(args[1]->type()); + if (args[0]->type() == STRING) { + return new ASTNativeCall(val.loc(), "_char_at", INT, args, arg_types); + } else { + err(val.loc(), "Accesses not implemented in AST yet."); + return error(); + } + } + if (!idx.is_int()) { + err(idx.loc(), "Expected integer index in accessor, given '", val.type(), "'."); + return error(); + } + if (val.is_string()) return Value(i64(val.get_string()[idx.get_int()])); + else if (val.is_product()) + return val.get_product()[idx.get_int()]; + else if (val.is_array()) + return val.get_array()[idx.get_int()]; + else { + err(val.loc(), "Cannot index into value of type '", val.type(), "'."); + return error(); + } + } + + Value strcat(const Value& a, const Value& b) { + if (a.is_error() || b.is_error()) return error(); + if (a.is_runtime() || b.is_runtime()) { + vector args; + Value al = lower(a), bl = lower(b); + args.push(al.get_runtime()); + args.push(bl.get_runtime()); + vector arg_types; + arg_types.push(STRING); + arg_types.push(STRING); + return new ASTNativeCall(a.loc(), "_strcat", STRING, args, arg_types); + } + if (!a.is_string() || !b.is_string()) { + err(a.loc(), "Expected string and string, given '", a.type(), "' and '", b.type(), "'."); + return error(); + } + return Value(a.get_string() + b.get_string(), STRING); + } + + Value substr(const Value& str, const Value& start, const Value& end) { + if (str.is_error() || start.is_error() || end.is_error()) return error(); + if (str.is_runtime() || start.is_runtime() || end.is_runtime()) { + vector args; + Value strl = lower(str), startl = lower(start), endl = lower(end); + args.push(strl.get_runtime()); + args.push(startl.get_runtime()); + args.push(endl.get_runtime()); + vector arg_types; + arg_types.push(STRING); + arg_types.push(INT); + arg_types.push(INT); + return new ASTNativeCall(str.loc(), "_substr", STRING, args, arg_types); + } + if (!str.is_string() || !start.is_int() || !end.is_int()) { + err(str.loc(), "Expected string, integer, and integer, given '", str.type(), "' and '", start.type(), + "' and '", end.type(), "'."); + return error(); + } + + return end.get_int() < start.get_int() + ? Value("", STRING) + : Value(string(str.get_string()[{start.get_int(), end.get_int()}]), STRING); + } + + Value type_of(const Value& v) { + return Value(v.type(), TYPE); + } + + Value cast(const Value& val, const Type* type) { + if (val.type() == type || type == ANY) return val; + + if (val.is_product()) { + if (type == TYPE) { + vector ts; + for (const Value& v : val.get_product()) ts.push(v.get_type()); + return Value(find(ts), TYPE); + } + } + + if (val.is_list() && type == TYPE) { + if (length(val) != Value(1l)) { + err(val.loc(), "Only single-element lists can be treated as types."); + return error(); + } + return find(head(val).get_type()); + } + + if (val.is_sum() && val.get_sum().value().type() == type) return val.get_sum().value(); + else if (val.is_sum()) { + err(val.loc(), "Sum value does not currently contain value of type '", type, "'."); + return error(); + } + + if (type->kind() == KIND_SUM) { return Value(new SumValue(val), val.type()); } + + err(val.loc(), "Could not convert value to type '", type, "'."); return error(); - } - - bool runtime_call = false; - for (u32 i = 0; i < argc; i ++) { - if (arg.get_product()[i].is_runtime()) runtime_call = true; - } - if (!fn.found_calls()) { - set visited; - find_calls(fn, env, fn.body(), visited); - } - if (fn.recursive()) runtime_call = true; - - if (runtime_call) { - vector argts; - vector lowered_args; - for (u32 i = 0; i < argc; i ++) { - if (fn.args()[i] & KEYWORD_ARG_BIT) { - // keyword arg - if (!arg.get_product()[i].is_symbol() || - arg.get_product()[i].get_symbol() != - (fn.args()[i] & ARG_NAME_MASK)) { - err(arg.get_product()[i].loc(), "Expected keyword '", - symbol_for(fn.args()[i] & ARG_NAME_MASK), "'."); - return error(); - } - } - else { - if (arg.get_product()[i].is_function()) { - vector inner_argts; - for (u32 j = 0; j < arg.get_product()[i].get_function().arity(); j ++) - inner_argts.push(find()); - argts.push(find( - find(inner_argts), find())); - lowered_args.push(arg.get_product()[i]); // we'll lower this later - } - else { - Value lowered = lower(arg.get_product()[i]); - argts.push(((const RuntimeType*)lowered.type())->base()); - lowered_args.push(lowered); - } - } - } - const Type* argt = find(argts); - ASTNode* body = fn.instantiation(argt); - if (!body) { - fn.instantiate(argt, new ASTIncompleteFn(function.loc(), argt, fn.name())); - body = instantiate(function.loc(), fn, argt); - } - if (!body) return error(); - vector arg_nodes; - for (u32 i = 0; i < lowered_args.size(); i ++) { - if (lowered_args[i].is_function()) { - const Type* t = ((const ProductType*)argt)->member(i); - if (t->kind() != KIND_FUNCTION || - !((const FunctionType*)t)->arg()->concrete()) { - err(lowered_args[i].loc(), "Could not deduce type for function ", - "parameter, resolved to '", t, "'."); - return error(); - } - const Type* fnarg = ((const FunctionType*)t)->arg(); - while (fnarg->kind() == KIND_TYPEVAR) - fnarg = ((const TypeVariable*)fnarg)->actual(); - FunctionValue& fn = lowered_args[i].get_function(); - ASTNode* argbody = fn.instantiation(fnarg); - if (!argbody) { - fn.instantiate(fnarg, new ASTIncompleteFn(lowered_args[i].loc(), - fnarg, fn.name())); - argbody = instantiate(lowered_args[i].loc(), fn, fnarg); - } - if (!argbody) return error(); - arg_nodes.push(argbody); - } - else arg_nodes.push(lowered_args[i].get_runtime()); - } - return new ASTCall(function.loc(), body, arg_nodes); - } - - for (u32 i = 0; i < arity; i ++) { - if (fn.args()[i] & KEYWORD_ARG_BIT) { - // keyword arg - if (!arg.get_product()[i].is_symbol() || - arg.get_product()[i].get_symbol() != - (fn.args()[i] & ARG_NAME_MASK)) { - err(arg.get_product()[i].loc(), "Expected keyword '", - symbol_for(fn.args()[i] & ARG_NAME_MASK), "'."); - return error(); - } - } + } + + Value is(const Value& val, const Value& type) { + if (!type.is_type()) { + err(type.loc(), "Expected type value in is-expression, given '", type.type(), "'."); + return error(); + } + if (val.type() == type.get_type()) return Value(true, BOOL); + else if (val.is_sum() && val.get_sum().value().type() == type.get_type()) + return Value(true, BOOL); + else + return Value(false, BOOL); + } + + Value as(const Value& val, const Value& type) { + if (!type.is_type()) { + err(type.loc(), "Expected type value in explicit cast, given '", type.type(), "'."); + return error(); + } + // if (val.is_runtime()) { + // return Value(new ASTAnnotate(val.loc(), val.get_runtime(), type.get_type())); + // } + // else if (val.type()->coerces_to(*type.get_type())) { + // err(val.loc(), "Could not unify value of type '", val.type(), "' with type '", + // type.get_type(), "'."); + // return error(); + // } + return cast(val, type.get_type()); + } + + Value annotate(const Value& val, const Value& type_in) { + Value type = type_in.is_type() ? type_in : annotate(type_in, Value(TYPE, TYPE)); + if (!type.is_type()) { + err(type.loc(), "Expected type value in annotation, given '", type.type(), "'."); + return error(); + } + if (val.is_runtime()) { + return Value(new ASTAnnotate(val.loc(), val.get_runtime(), type.get_type())); + } else if (!val.type()->coerces_to(type.get_type())) { + err(val.loc(), "Could not unify value of type '", val.type(), "' with type '", type.get_type(), "'."); + return error(); + } + return cast(val, type.get_type()); + } + + ASTNode* instantiate(SourceLocation loc, FunctionValue& fn, const Type* args_type) { + ref new_env = fn.get_env()->clone(); + new_env->make_runtime(); + u32 j = 0; + vector new_args; + for (u32 i = 0; i < fn.arity(); i++) { + if (!(fn.args()[i] & KEYWORD_ARG_BIT)) { + auto it = new_env->find(symbol_for(fn.args()[i] & ARG_NAME_MASK)); + const Type* argt = ((const ProductType*)args_type)->member(j); + it->value = new ASTSingleton(argt); + j++; + new_args.push(fn.args()[i]); + } + } + Value cloned = fn.body().clone(); + prep(new_env, cloned); + Value v = eval(new_env, cloned); + if (v.is_error()) return nullptr; + if (!v.is_runtime()) v = lower(v); + ASTNode* result = new ASTFunction(loc, new_env, args_type, new_args, v.get_runtime(), fn.name()); + fn.instantiate(args_type, result); + return result; + } + + void find_calls(FunctionValue& fn, ref env, const Value& term, set& visited) { + if (!term.is_list()) return; + Value h = head(term); + if (h.is_symbol()) { + Def* def = env->find(symbol_for(h.get_symbol())); + if (def && def->value.is_function()) { + FunctionValue* f = &def->value.get_function(); + if (visited.find(f) == visited.end()) { + visited.insert(f); + if (f != &fn) find_calls(*f, f->get_env(), f->body(), visited); + fn.add_call(f); + } + } + } + if (!introduces_env(term)) { + const Value* v = &term; + while (v->is_list()) { + find_calls(fn, env, v->get_list().head(), visited); + v = &v->get_list().tail(); + } + } + } + + Value call(ref env, Value& callable, const Value& args) { + Value function = callable; + if (function.is_intersect()) { + map ftypes; + i64 coerced_priority = args.get_product().size() + 1, + exact_priority = coerced_priority * coerced_priority; + for (const auto& p : function.get_intersect()) { + if (p.first->kind() != KIND_FUNCTION) continue; + const ProductType* fnargst = (const ProductType*)((const FunctionType*)p.first)->arg(); + if (fnargst->count() != args.get_product().size()) continue; + i64 priority = 0; + const ProductType* argst = (const ProductType*)args.type(); + for (u32 i = 0; i < argst->count(); i ++) { + if (argst->member(i) == fnargst->member(i)) + priority += exact_priority; + else if (fnargst->member(i) != ANY && argst->member(i)->coerces_to(fnargst->member(i))) + priority += coerced_priority; + else if (fnargst->member(i) != ANY) // implies argst[i] cannot coerce to desired type, incompatible + goto end; + } + ftypes.put((const FunctionType*)p.first, priority); + end:; + } + + if (ftypes.size() == 0) { + err(function.loc(), "No overload of '", function, "' matches argument types ", args.type(), "."); + return error(); + } + if (ftypes.size() > 1) { + i64 max = 0; + for (const auto& p : ftypes) if (p.second > max) max = p.second; + set to_remove; + for (const auto& p : ftypes) if (p.second < max) to_remove.insert(p.first); + for (const FunctionType* t : to_remove) ftypes.erase(t); + + if (ftypes.size() > 1) { + err(function.loc(), "Call to '", function, "' is ambiguous; multiple overloads match arguments ", + args.type(), "."); + return error(); + } + } + + function = function.get_intersect().values()[ftypes.begin()->first]; + } + + if (!function.is_function()) { + err(function.loc(), "Cannot call non-function value '", function, "'."); + return error(); + } + if (!args.is_product()) { + err(function.loc(), "Cannot call function with non-product argument '", args, "'."); + return error(); + } + + const FunctionType* ft = (const FunctionType*)function.type(); + const ProductType* argst = (const ProductType*)ft->arg(); + + if (args.get_product().size() != argst->count()) { + err(function.loc(), "Wrong number of arguments for function '", function, "': expected ", argst->count(), + " arguments, given ", args.get_product().size(), "."); + return error(); + } + + // Handle runtime arguments. + + Value args_copy = args; + for (u32 i = 0; i < argst->count(); i++) { + const Value& arg = args.get_product()[i]; + const Type* argt = arg.type(); + if (!argt->coerces_to(argst->member(i))) { + err(arg.loc(), "Incorrectly typed argument for function '", function, "' at position ", i, + ": expected '", argst->member(i), "', given '", argt, "'."); + return error(); + } + if (argt != argst->member(i)) args_copy.get_product()[i] = cast(args.get_product()[i], argst->member(i)); + } + + FunctionValue& fn = function.get_function(); + if (fn.is_builtin()) { + return fn.get_builtin().eval(env, args_copy); + } else { + ref fnenv = fn.get_env(); + for (u32 i = 0; i < fn.arity(); i++) { + if (fn.args()[i] & KEYWORD_ARG_BIT) { + // keyword arg + if (!args_copy.get_product()[i].is_symbol() || + args_copy.get_product()[i].get_symbol() != (fn.args()[i] & ARG_NAME_MASK)) { + err(args_copy.get_product()[i].loc(), "Expected keyword '", + symbol_for(fn.args()[i] & ARG_NAME_MASK), "'."); + return error(); + } + } else { + const string& argname = symbol_for(fn.args()[i] & ARG_NAME_MASK); + fnenv->find(argname)->value = args_copy.get_product()[i]; + } + } + Value prepped = fn.body().clone(); + prep(fnenv, prepped); + return eval(fnenv, prepped); + } + } + + Value call_old(ref env, Value& function, const Value& arg) { + if (function.is_runtime()) { + u32 argc = arg.get_product().size(); + vector argts; + vector lowered_args; + for (u32 i = 0; i < argc; i++) { + if (arg.get_product()[i].is_function()) { + vector inner_argts; + for (u32 j = 0; j < arg.get_product()[i].get_function().arity(); j++) + inner_argts.push(find()); + argts.push(find(find(inner_argts), find())); + lowered_args.push(arg.get_product()[i]); // we'll lower this later + } else { + Value lowered = lower(arg.get_product()[i]); + argts.push(((const RuntimeType*)lowered.type())->base()); + lowered_args.push(lowered); + } + } + const Type* argt = find(argts); + vector arg_nodes; + for (u32 i = 0; i < lowered_args.size(); i++) { + if (lowered_args[i].is_function()) { + const Type* t = ((const ProductType*)argt)->member(i); + if (!t->concrete() || t->kind() != KIND_FUNCTION) { + err(lowered_args[i].loc(), "Could not deduce type for function ", "parameter, resolved to '", t, + "'."); + return error(); + } + const Type* fnarg = ((const FunctionType*)t)->arg(); + FunctionValue& fn = lowered_args[i].get_function(); + ASTNode* argbody = fn.instantiation(fnarg); + if (!argbody) { + fn.instantiate(fnarg, new ASTIncompleteFn(lowered_args[i].loc(), fnarg, fn.name())); + argbody = instantiate(lowered_args[i].loc(), fn, fnarg); + } + if (!argbody) return error(); + arg_nodes.push(argbody); + } else + arg_nodes.push(lowered_args[i].get_runtime()); + } + return new ASTCall(function.loc(), function.get_runtime(), arg_nodes); + } + + if (!function.is_function() && !function.is_error()) { + err(function.loc(), "Called value is not a procedure."); + return error(); + } + if (!arg.is_product() && !arg.is_error()) { + err(arg.loc(), "Arguments not provided as a product."); + return error(); + } + if (function.is_error() || arg.is_error()) return error(); + + FunctionValue& fn = function.get_function(); + if (fn.is_builtin()) { + return fn.get_builtin().eval(env, arg); + } else { + ref env = fn.get_env(); + u32 argc = arg.get_product().size(), arity = fn.args().size(); + if (argc != arity) { + err(function.loc(), "Procedure requires ", arity, " arguments, ", argc, " provided."); + return error(); + } + + bool runtime_call = false; + for (u32 i = 0; i < argc; i++) { + if (arg.get_product()[i].is_runtime()) runtime_call = true; + } + if (!fn.found_calls()) { + set visited; + find_calls(fn, env, fn.body(), visited); + } + if (fn.recursive()) runtime_call = true; + + if (runtime_call) { + vector argts; + vector lowered_args; + for (u32 i = 0; i < argc; i++) { + if (fn.args()[i] & KEYWORD_ARG_BIT) { + // keyword arg + if (!arg.get_product()[i].is_symbol() || + arg.get_product()[i].get_symbol() != (fn.args()[i] & ARG_NAME_MASK)) { + err(arg.get_product()[i].loc(), "Expected keyword '", + symbol_for(fn.args()[i] & ARG_NAME_MASK), "'."); + return error(); + } + } else { + if (arg.get_product()[i].is_function()) { + vector inner_argts; + for (u32 j = 0; j < arg.get_product()[i].get_function().arity(); j++) + inner_argts.push(find()); + argts.push(find(find(inner_argts), find())); + lowered_args.push(arg.get_product()[i]); // we'll lower this later + } else { + Value lowered = lower(arg.get_product()[i]); + argts.push(((const RuntimeType*)lowered.type())->base()); + lowered_args.push(lowered); + } + } + } + const Type* argt = find(argts); + ASTNode* body = fn.instantiation(argt); + if (!body) { + fn.instantiate(argt, new ASTIncompleteFn(function.loc(), argt, fn.name())); + body = instantiate(function.loc(), fn, argt); + } + if (!body) return error(); + vector arg_nodes; + for (u32 i = 0; i < lowered_args.size(); i++) { + if (lowered_args[i].is_function()) { + const Type* t = ((const ProductType*)argt)->member(i); + if (t->kind() != KIND_FUNCTION || !((const FunctionType*)t)->arg()->concrete()) { + err(lowered_args[i].loc(), "Could not deduce type for function ", + "parameter, resolved to '", t, "'."); + return error(); + } + const Type* fnarg = ((const FunctionType*)t)->arg(); + while (fnarg->kind() == KIND_TYPEVAR) fnarg = ((const TypeVariable*)fnarg)->actual(); + FunctionValue& fn = lowered_args[i].get_function(); + ASTNode* argbody = fn.instantiation(fnarg); + if (!argbody) { + fn.instantiate(fnarg, new ASTIncompleteFn(lowered_args[i].loc(), fnarg, fn.name())); + argbody = instantiate(lowered_args[i].loc(), fn, fnarg); + } + if (!argbody) return error(); + arg_nodes.push(argbody); + } else + arg_nodes.push(lowered_args[i].get_runtime()); + } + return new ASTCall(function.loc(), body, arg_nodes); + } + + for (u32 i = 0; i < arity; i++) { + if (fn.args()[i] & KEYWORD_ARG_BIT) { + // keyword arg + if (!arg.get_product()[i].is_symbol() || + arg.get_product()[i].get_symbol() != (fn.args()[i] & ARG_NAME_MASK)) { + err(arg.get_product()[i].loc(), "Expected keyword '", symbol_for(fn.args()[i] & ARG_NAME_MASK), + "'."); + return error(); + } + } else { + const string& argname = symbol_for(fn.args()[i] & ARG_NAME_MASK); + env->find(argname)->value = arg.get_product()[i]; + } + } + Value prepped = fn.body().clone(); + prep(env, prepped); + return eval(env, prepped); + } + } + + Value display(const Value& arg) { + return new ASTDisplay(arg.loc(), lower(arg).get_runtime()); + } + + Value assign(ref env, const Value& dest, const Value& src) { + if (!dest.is_symbol()) { + err(dest.loc(), "Invalid destination in assignment '", dest, "'."); + return error(); + } + + Def* def = env->find(symbol_for(dest.get_symbol())); + if (!def) { + err(dest.loc(), "Undefined variable '", symbol_for(dest.get_symbol()), "'."); + return error(); + } + Value lowered = src; + if (!lowered.is_runtime()) lowered = lower(src); + if (def->value.is_runtime()) return new ASTAssign(dest.loc(), env, dest.get_symbol(), lowered.get_runtime()); else { - const string& argname = symbol_for(fn.args()[i] & ARG_NAME_MASK); - env->find(argname)->value = arg.get_product()[i]; - } - } - Value prepped = fn.body().clone(); - prep(env, prepped); - return eval(env, prepped); - } - } - - Value display(const Value& arg) { - return new ASTDisplay(arg.loc(), lower(arg).get_runtime()); - } - - Value assign(ref env, const Value &dest, const Value& src) { - if (!dest.is_symbol()) { - err(dest.loc(), "Invalid destination in assignment '", dest, "'."); - return error(); - } - - Def* def = env->find(symbol_for(dest.get_symbol())); - if (!def) { - err(dest.loc(), "Undefined variable '", - symbol_for(dest.get_symbol()), "'."); - return error(); - } - Value lowered = src; - if (!lowered.is_runtime()) lowered = lower(src); - if (def->value.is_runtime()) - return new ASTAssign(dest.loc(), env, - dest.get_symbol(), lowered.get_runtime()); - else { - def->value = lower(def->value); - return new ASTDefine(dest.loc(), env, - dest.get_symbol(), lowered.get_runtime()); - } - } -} + def->value = lower(def->value); + return new ASTDefine(dest.loc(), env, dest.get_symbol(), lowered.get_runtime()); + } + } +} // namespace basil -template<> +template <> u64 hash(const basil::Value& t) { - return t.hash(); + return t.hash(); } void write(stream& io, const basil::Value& t) { - t.format(io); + t.format(io); } \ No newline at end of file diff --git a/compiler/values.h b/compiler/values.h index 1cd48ce..4556ab7 100644 --- a/compiler/values.h +++ b/compiler/values.h @@ -13,12 +13,14 @@ namespace basil { class Def; class Env; class ASTNode; + class Builtin; u64 symbol_value(const string& symbol); const string& symbol_for(u64 value); class ListValue; class SumValue; + class IntersectValue; class ProductValue; class ArrayValue; class FunctionValue; @@ -30,6 +32,7 @@ namespace basil { union { i64 i; u64 u; + double f; const Type* t; bool b; RC* rc; @@ -39,13 +42,16 @@ namespace basil { Value(); Value(const Type* type); Value(i64 i, const Type* type = INT); + // Value(double d); Value(const string& s, const Type* type = SYMBOL); Value(const Type* type_value, const Type* type); Value(ListValue* l); Value(SumValue* s, const Type* type); + Value(IntersectValue* i, const Type* type); Value(ProductValue* p); Value(ArrayValue* a); - Value(FunctionValue* f); + Value(ref env, const Builtin& b); + Value(FunctionValue* f, const Type* ftype); Value(AliasValue* f); Value(MacroValue* f); Value(ASTNode* n); @@ -57,6 +63,10 @@ namespace basil { i64 get_int() const; i64& get_int(); + bool is_float() const; + double get_float() const; + double& get_float(); + bool is_symbol() const; u64 get_symbol() const; u64& get_symbol(); @@ -89,6 +99,10 @@ namespace basil { const SumValue& get_sum() const; SumValue& get_sum(); + bool is_intersect() const; + const IntersectValue& get_intersect() const; + IntersectValue& get_intersect(); + bool is_product() const; const ProductValue& get_product() const; ProductValue& get_product(); @@ -149,6 +163,21 @@ namespace basil { const Value& value() const; }; + class IntersectValue : public RC { + map _values; + public: + IntersectValue(const map& values); + + u32 size() const; + bool has(const Type* t) const; + map::const_iterator begin() const; + map::const_iterator end() const; + map::iterator begin(); + map::iterator end(); + const map& values() const; + map& values(); + }; + class ProductValue : public RC { vector _values; public: @@ -177,17 +206,15 @@ namespace basil { class FunctionValue : public RC { i64 _name; Value _code; - BuiltinFn _builtin; + const Builtin* _builtin; ref _env; - u64 _builtin_arity; vector _args; set* _calls; map* _insts; public: - FunctionValue(ref env, const vector& args, + FunctionValue(ref env, const vector& args, const Value& code, i64 name = -1); - FunctionValue(ref env, BuiltinFn builtin, u64 arity, - i64 name = -1); + FunctionValue(ref env, const Builtin& builtin, i64 name = -1); ~FunctionValue(); FunctionValue(const FunctionValue& other); FunctionValue& operator=(const FunctionValue& other); @@ -196,7 +223,7 @@ namespace basil { const Value& body() const; bool is_builtin() const; u64 arity() const; - BuiltinFn get_builtin() const; + const Builtin& get_builtin() const; ref get_env(); const ref get_env() const; i64 name() const; @@ -220,20 +247,19 @@ namespace basil { class MacroValue : public RC { Value _code; - BuiltinMacro _builtin; + const Builtin* _builtin; ref _env; - u64 _builtin_arity; vector _args; public: MacroValue(ref env, const vector& args, const Value& code); - MacroValue(ref env, BuiltinFn builtin, u64 arity); + MacroValue(ref env, const Builtin& builtin); const vector& args() const; const Value& body() const; bool is_builtin() const; u64 arity() const; - BuiltinFn get_builtin() const; + const Builtin& get_builtin() const; ref get_env(); const ref get_env() const; }; @@ -297,6 +323,7 @@ namespace basil { const Type* args_type); Value type_of(const Value& v); Value is(const Value& v, const Value& t); + Value cast(const Value& val, const Type* type); Value annotate(const Value& val, const Value& type); Value as(const Value& v, const Value& t); Value call(ref env, Value& function, const Value& arg); diff --git a/util/hash.h b/util/hash.h index c02bb15..0a72d62 100644 --- a/util/hash.h +++ b/util/hash.h @@ -394,4 +394,50 @@ class map : public set> { } }; +template +void fill_set(set& s) { + // +} + +template +void fill_set(set& s, const T& t) { + s.insert(t); +} + +template +void fill_set(set& s, const T& t, const Args&... args) { + s.insert(t); + fill_set(s, args...); +} + +template +set set_of(const Args&... args) { + set s; + fill_set(s, args...); + return s; +} + +template +void fill_map(map& m) { + // +} + +template +void fill_map(map& m, const K& k, const V& v) { + m.put(k, v); +} + +template +void fill_map(map& m, const K& k, const V& v, const Args&... args) { + m.put(k, v); + fill_map(m, args...); +} + +template +map map_of(const Args&... args) { + map m; + fill_map(m, args...); + return m; +} + #endif diff --git a/util/vec.h b/util/vec.h index ae75735..cf98a9c 100644 --- a/util/vec.h +++ b/util/vec.h @@ -143,6 +143,11 @@ class vector { } }; +template +void fill_vector(vector& v) { + // +} + template void fill_vector(vector& v, const T& t) { v.push(t); From 2304790f97ea8a9f58e0e773448517cb34938ba2 Mon Sep 17 00:00:00 2001 From: elucent <9532786+elucent@users.noreply.github.com> Date: Wed, 27 Jan 2021 12:24:32 -0500 Subject: [PATCH 08/17] Expanded tuple and array types, indexing, as well as revamped function declaration syntax. Also, compiling to ELF works now! --- .gitignore | 3 +- Makefile | 12 +- compiler/ast.cpp | 7 +- compiler/ast.h | 1 + compiler/builtin.cpp | 274 ++++++++++++------ compiler/builtin.h | 12 +- compiler/driver.cpp | 57 +++- compiler/eval.cpp | 187 ++++++++----- compiler/ir.cpp | 7 +- compiler/lex.cpp | 15 +- compiler/lex.h | 6 +- compiler/parse.cpp | 522 +++++++++++++++++------------------ compiler/type.cpp | 43 +++ compiler/type.h | 19 +- compiler/values.cpp | 113 +++++++- compiler/values.h | 2 + example/arrays.bl | 2 + example/fibonacci.bl | 3 +- example/fibonacci.c | 5 +- example/fibonaccic | Bin 9248 -> 0 bytes jasmine/obj.cpp | 49 +++- jasmine/obj.h | 1 + jasmine/x64.h | 2 +- runtime.cpp | 0 core.cpp => runtime/core.cpp | 78 ++++-- runtime/sys.cpp | 318 +++++++++++++++++++++ runtime/sys.h | 71 +++++ util/str.cpp | 4 + util/str.h | 1 + 29 files changed, 1313 insertions(+), 501 deletions(-) create mode 100644 example/arrays.bl delete mode 100644 example/fibonaccic delete mode 100644 runtime.cpp rename core.cpp => runtime/core.cpp (67%) create mode 100644 runtime/sys.cpp create mode 100644 runtime/sys.h diff --git a/.gitignore b/.gitignore index a71774d..2bd7ea1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .vscode/ *.o TODO -basil \ No newline at end of file +basil +librt.a diff --git a/Makefile b/Makefile index a624a0b..68e91f7 100644 --- a/Makefile +++ b/Makefile @@ -6,8 +6,8 @@ OBJS := $(patsubst %.cpp,%.o,$(SRCS)) CXX := clang++ CXXHEADERS := -I. -Iutil -Ijasmine -Icompiler -CXXFLAGS := $(CXXHEADERS) -std=c++17 -ffast-math -fno-rtti -fno-exceptions -Wno-null-dereference -LDFLAGS := -Wl,--unresolved-symbols=ignore-in-object-files +CXXFLAGS := $(CXXHEADERS) -std=c++11 -Os -ffast-math -fno-rtti -fno-exceptions -Wno-null-dereference +LDFLAGS := -Wl,--unresolved-symbols=ignore-in-object-files clean: rm -f $(OBJS) *.o.tmp basil @@ -20,7 +20,13 @@ basil: $(OBJS) $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ release: $(OBJS) - $(CXX) $(CXXFLAGS) $^ -o basil + $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o basil + +librt.a: runtime/core.o runtime/sys.o + ar r $@ $^ + +runtime/%.o: runtime/%.cpp + $(CXX) -I. -Iutil -std=c++11 -fPIC -Os -ffast-math -fno-rtti -fno-exceptions -Os -nostdlib -nostdlib++ -c $< -o $@ %.o: %.cpp %.h $(CXX) $(CXXFLAGS) -c $< -o $@ diff --git a/compiler/ast.cpp b/compiler/ast.cpp index 3ed008d..107db88 100644 --- a/compiler/ast.cpp +++ b/compiler/ast.cpp @@ -583,6 +583,7 @@ namespace basil { Location ASTFunction::emit(Function& func) { if (!_emitted) { + _emitted = true; Function& fn = _name == -1 ? func.create_function() : func.create_function(symbol_for(_name)); _label = fn.label(); @@ -593,8 +594,6 @@ namespace basil { } fn.add(new RetInsn(_body->emit(fn))); fn.last()->succ().clear(); - - _emitted = true; } Location loc; @@ -608,6 +607,10 @@ namespace basil { else write(io, symbol_for(_name)); } + u32 ASTFunction::label() const { + return _label; + } + ASTBlock::ASTBlock(SourceLocation loc, const vector& exprs): ASTNode(loc), _exprs(exprs) { for (ASTNode* n : _exprs) n->inc(); diff --git a/compiler/ast.h b/compiler/ast.h index b116b84..9c5688d 100644 --- a/compiler/ast.h +++ b/compiler/ast.h @@ -291,6 +291,7 @@ namespace basil { ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; + u32 label() const; }; class ASTBlock : public ASTNode { diff --git a/compiler/builtin.cpp b/compiler/builtin.cpp index b5e6edf..f73554a 100644 --- a/compiler/builtin.cpp +++ b/compiler/builtin.cpp @@ -1,14 +1,15 @@ #include "builtin.h" -#include "type.h" -#include "values.h" #include "ast.h" #include "env.h" +#include "eval.h" +#include "type.h" +#include "values.h" namespace basil { Builtin::Builtin() {} - Builtin::Builtin(const Type* type, Function eval, Function compile): - _type(type), _eval(eval), _compile(compile) {} + Builtin::Builtin(const Type* type, Function eval, Function compile, BuiltinFlags flags) + : _type(type), _eval(eval), _compile(compile), _flags(flags) {} const Type* Builtin::type() const { return _type; @@ -22,126 +23,212 @@ namespace basil { return _compile(env, args); } - #define ARG(n) args.get_product()[n] + bool Builtin::should_lower() const { + return !(_flags & NO_AUTO_LOWER); + } - Builtin - ADD, SUB, MUL, DIV, REM, - AND, OR, XOR, NOT, - EQUALS, NOT_EQUALS, LESS, GREATER, LESS_EQUAL, GREATER_EQUAL, - IS_EMPTY, HEAD, TAIL, CONS, - DISPLAY, READ_LINE, READ_WORD, READ_INT, - LENGTH, AT, STRCAT, SUBSTR, - ANNOTATE, TYPEOF, LIST_TYPE, - ASSIGN, IF; + bool Builtin::runtime_only() const { + return !_eval; + } + +#define ARG(n) args.get_product()[n] + + Builtin ADD, SUB, MUL, DIV, REM, AND, OR, XOR, NOT, EQUALS, NOT_EQUALS, LESS, GREATER, LESS_EQUAL, GREATER_EQUAL, + IS_EMPTY, HEAD, TAIL, CONS, DISPLAY, READ_LINE, READ_WORD, READ_INT, LENGTH, + AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, STRCAT, SUBSTR, ANNOTATE, TYPEOF, LIST_TYPE, ASSIGN, IF; static void init_builtins() { - ADD = { - find(find(INT, INT), INT), - [](ref env, const Value& args) -> Value { - return Value(ARG(0).get_int() + ARG(1).get_int()); - }, - [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryMath(ARG(0).loc(), AST_ADD, - ARG(0).get_runtime(), ARG(1).get_runtime())); + ADD = {find(find(INT, INT), INT), + [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() + ARG(1).get_int()); }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryMath(ARG(0).loc(), AST_ADD, ARG(0).get_runtime(), ARG(1).get_runtime())); + }}; + SUB = {find(find(INT, INT), INT), + [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() - ARG(1).get_int()); }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryMath(ARG(0).loc(), AST_SUB, ARG(0).get_runtime(), ARG(1).get_runtime())); + }}; + MUL = {find(find(INT, INT), INT), + [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() * ARG(1).get_int()); }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryMath(ARG(0).loc(), AST_MUL, ARG(0).get_runtime(), ARG(1).get_runtime())); + }}; + DIV = {find(find(INT, INT), INT), + [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() / ARG(1).get_int()); }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryMath(ARG(0).loc(), AST_DIV, ARG(0).get_runtime(), ARG(1).get_runtime())); + }}; + REM = {find(find(INT, INT), INT), + [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() % ARG(1).get_int()); }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryMath(ARG(0).loc(), AST_REM, ARG(0).get_runtime(), ARG(1).get_runtime())); + }}; + AND = {find(find(BOOL, BOOL), BOOL), + [](ref env, const Value& args) -> Value { + return Value(ARG(0).get_bool() && ARG(1).get_bool(), BOOL); + }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryLogic(ARG(0).loc(), AST_AND, ARG(0).get_runtime(), ARG(1).get_runtime())); + }}; + OR = {find(find(BOOL, BOOL), BOOL), + [](ref env, const Value& args) -> Value { + return Value(ARG(0).get_bool() || ARG(1).get_bool(), BOOL); + }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryLogic(ARG(0).loc(), AST_OR, ARG(0).get_runtime(), ARG(1).get_runtime())); + }}; + XOR = { + find(find(BOOL, BOOL), BOOL), + [](ref env, const Value& args) -> Value { return Value(ARG(0).get_bool() ^ ARG(1).get_bool(), BOOL); }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryLogic(ARG(0).loc(), AST_XOR, ARG(0).get_runtime(), ARG(1).get_runtime())); + }}; + NOT = {find(find(BOOL), BOOL), + [](ref env, const Value& args) -> Value { return Value(!ARG(0).get_bool(), BOOL); }, + [](ref env, const Value& args) -> Value { + return Value(new ASTNot(ARG(0).loc(), ARG(0).get_runtime())); + }}; + EQUALS = {find(find(ANY, ANY), BOOL), + [](ref env, const Value& args) -> Value { return Value(ARG(0) == ARG(1), BOOL); }, + [](ref env, const Value& args) -> Value { + return Value( + new ASTBinaryEqual(ARG(0).loc(), AST_EQUAL, ARG(0).get_runtime(), ARG(1).get_runtime())); + }}; + NOT_EQUALS = { + find(find(ANY, ANY), BOOL), + [](ref env, const Value& args) -> Value { return Value(ARG(0) != ARG(1), BOOL); }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryEqual(ARG(0).loc(), AST_INEQUAL, ARG(0).get_runtime(), ARG(1).get_runtime())); + }}; + LESS = { + find(find(INT, INT), BOOL), + [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() < ARG(1).get_int(), BOOL); }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryRel(ARG(0).loc(), AST_LESS, ARG(0).get_runtime(), ARG(1).get_runtime())); } }; - SUB = { - find(find(INT, INT), INT), - [](ref env, const Value& args) -> Value { - return Value(ARG(0).get_int() - ARG(1).get_int()); - }, + LESS_EQUAL = { + find(find(INT, INT), BOOL), + [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() <= ARG(1).get_int(), BOOL); }, [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryMath(ARG(0).loc(), AST_SUB, - ARG(0).get_runtime(), ARG(1).get_runtime())); + return Value( + new ASTBinaryRel(ARG(0).loc(), AST_LESS_EQUAL, ARG(0).get_runtime(), ARG(1).get_runtime())); } }; - MUL = { - find(find(INT, INT), INT), - [](ref env, const Value& args) -> Value { - return Value(ARG(0).get_int() * ARG(1).get_int()); - }, + GREATER = { + find(find(INT, INT), BOOL), + [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() > ARG(1).get_int(), BOOL); }, [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryMath(ARG(0).loc(), AST_MUL, - ARG(0).get_runtime(), ARG(1).get_runtime())); + return Value(new ASTBinaryRel(ARG(0).loc(), AST_GREATER, ARG(0).get_runtime(), ARG(1).get_runtime())); } }; - DIV = { - find(find(INT, INT), INT), + GREATER_EQUAL = { + find(find(INT, INT), BOOL), + [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() >= ARG(1).get_int(), BOOL); }, [](ref env, const Value& args) -> Value { - return Value(ARG(0).get_int() / ARG(1).get_int()); - }, - [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryMath(ARG(0).loc(), AST_DIV, - ARG(0).get_runtime(), ARG(1).get_runtime())); + return Value( + new ASTBinaryRel(ARG(0).loc(), AST_GREATER_EQUAL, ARG(0).get_runtime(), ARG(1).get_runtime())); } }; - REM = { - find(find(INT, INT), INT), + DISPLAY = { + find(find(ANY), VOID), + nullptr, [](ref env, const Value& args) -> Value { - return Value(ARG(0).get_int() % ARG(1).get_int()); - }, - [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryMath(ARG(0).loc(), AST_REM, - ARG(0).get_runtime(), ARG(1).get_runtime())); + return Value(new ASTDisplay(ARG(0).loc(), ARG(0).get_runtime())); } }; - AND = { - find(find(BOOL, BOOL), BOOL), - [](ref env, const Value& args) -> Value { - return Value(ARG(0).get_bool() && ARG(1).get_bool(), BOOL); + AT_INT = { + find(find(ANY, INT), ANY), + [](ref env, const Value& args) -> Value { + if (ARG(0).is_string()) return Value(ARG(0).get_string()[ARG(1).get_int()], INT); + if (ARG(0).is_product()) return ARG(0).get_product()[ARG(1).get_int()]; + if (ARG(0).is_array()) return ARG(0).get_array()[ARG(1).get_int()]; + err(ARG(0).loc(), "Cannot index into value of type '", ARG(0).type(), "'."); + return error(); }, - [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryLogic(ARG(0).loc(), AST_AND, - ARG(0).get_runtime(), ARG(1).get_runtime())); - } + nullptr // todo: runtime indexing }; - OR = { - find(find(BOOL, BOOL), BOOL), - [](ref env, const Value& args) -> Value { - return Value(ARG(0).get_bool() || ARG(1).get_bool(), BOOL); + AT_LIST = { + find(find(ANY, find(INT)), ANY), + [](ref env, const Value& args) -> Value { + if (ARG(0).is_string()) { + string result = ""; + Value indexlist = ARG(1); + while (!indexlist.is_void()) { + result += ARG(0).get_string()[head(indexlist).get_int()]; + indexlist = tail(indexlist); + } + return Value(result, STRING); + } + if (ARG(0).is_product()) { + vector values; + Value indexlist = ARG(1); + while (!indexlist.is_void()) { + values.push(ARG(0).get_product()[head(indexlist).get_int()]); + indexlist = tail(indexlist); + } + return Value(new ProductValue(values)); + } + if (ARG(0).is_array()) { + vector values; + Value indexlist = ARG(1); + while (!indexlist.is_void()) { + values.push(ARG(0).get_array()[head(indexlist).get_int()]); + indexlist = tail(indexlist); + } + return Value(new ArrayValue(values)); + } + err(ARG(0).loc(), "Cannot index into value of type '", ARG(0).type(), "'."); + return error(); }, - [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryLogic(ARG(0).loc(), AST_OR, - ARG(0).get_runtime(), ARG(1).get_runtime())); - } + nullptr // todo: runtime indexing }; - XOR = { - find(find(BOOL, BOOL), BOOL), + AT_ARRAY_TYPE = { + find(find(TYPE, INT), TYPE), [](ref env, const Value& args) -> Value { - return Value(ARG(0).get_bool() ^ ARG(1).get_bool(), BOOL); + return Value(find(ARG(0).get_type(), ARG(1).get_int()), TYPE); }, - [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryLogic(ARG(0).loc(), AST_XOR, - ARG(0).get_runtime(), ARG(1).get_runtime())); - } - }; - NOT = { - find(find(BOOL), BOOL), - [](ref env, const Value& args) -> Value { - return Value(!ARG(0).get_bool(), BOOL); - }, - [](ref env, const Value& args) -> Value { - return Value(new ASTNot(ARG(0).loc(), ARG(0).get_runtime())); - } + nullptr }; - TYPEOF = { - find(find(ANY), TYPE), + AT_DYNARRAY_TYPE = { + find(find(TYPE, VOID), TYPE), [](ref env, const Value& args) -> Value { - return Value(ARG(0).type(), TYPE); + return Value(find(ARG(0).get_type()), TYPE); }, nullptr }; + TYPEOF = {find(find(ANY), TYPE), + [](ref env, const Value& args) -> Value { return Value(ARG(0).type(), TYPE); }, nullptr}; LIST_TYPE = { find(find(TYPE), TYPE), - [](ref env, const Value& args) -> Value { - return Value(find(ARG(0).get_type()), TYPE); + [](ref env, const Value& args) -> Value { return Value(find(ARG(0).get_type()), TYPE); }, + nullptr}; + + IF = { + find(find(BOOL, ANY, ANY), ANY), + [](ref env, const Value& args) -> Value { return eval(env, ARG(ARG(0).get_bool() ? 1 : 2)); }, + [](ref env, const Value& args) -> Value { + Value left = eval(env, ARG(1)), right = eval(env, ARG(2)); + if (left.is_error() || right.is_error()) return error(); + if (!left.is_runtime()) left = lower(left); + if (!right.is_runtime()) right = lower(right); + return new ASTIf(ARG(0).loc(), ARG(0).get_runtime(), left.get_runtime(), right.get_runtime()); }, - nullptr + NO_AUTO_LOWER }; } static bool inited = false; + template + Value cases(ref env, const Args... args) { + vector vals = vector_of(args...); + set ts; + map m; + for (Builtin* v : vals) ts.insert(v->type()), m.put(v->type(), Value(env, *v)); + return Value(new IntersectValue(m), find(ts)); + } + void define_builtins(ref env) { if (!inited) inited = true, init_builtins(); env->infix("+", Value(env, ADD), 2, 20); @@ -153,7 +240,16 @@ namespace basil { env->infix("or", Value(env, OR), 2, 5); env->infix("xor", Value(env, XOR), 2, 5); env->def("not", Value(env, NOT), 1); + env->infix("==", Value(env, EQUALS), 2, 10); + env->infix("!=", Value(env, NOT_EQUALS), 2, 10); + env->infix("<", Value(env, LESS), 2, 10); + env->infix(">", Value(env, GREATER), 2, 10); + env->infix("<=", Value(env, LESS_EQUAL), 2, 10); + env->infix(">=", Value(env, GREATER_EQUAL), 2, 10); + env->def("display", Value(env, DISPLAY), 1); + env->infix("at", cases(env, &AT_INT, &AT_LIST, &AT_ARRAY_TYPE, &AT_DYNARRAY_TYPE), 2, 120); env->def("typeof", Value(env, TYPEOF), 1); env->infix("list", Value(env, LIST_TYPE), 1, 80); + env->infix("#?", Value(env, IF), 3, 2); } -} \ No newline at end of file +} // namespace basil \ No newline at end of file diff --git a/compiler/builtin.h b/compiler/builtin.h index e7efa6f..cce5005 100644 --- a/compiler/builtin.h +++ b/compiler/builtin.h @@ -9,17 +9,25 @@ namespace basil { class Value; class Env; + enum BuiltinFlags { + AUTO_LOWER = 0, + NO_AUTO_LOWER = 1 + }; + class Builtin { using Function = Value(*)(ref, const Value&); const Type* _type; Function _eval, _compile; + u32 _flags; public: Builtin(); - Builtin(const Type* type, Function eval, Function compile); + Builtin(const Type* type, Function eval, Function compile, BuiltinFlags flags = AUTO_LOWER); const Type* type() const; Value eval(ref env, const Value& args) const; Value compile(ref env, const Value& args) const; + bool should_lower() const; + bool runtime_only() const; }; extern void define_builtins(ref env); @@ -30,7 +38,7 @@ namespace basil { EQUALS, NOT_EQUALS, LESS, GREATER, LESS_EQUAL, GREATER_EQUAL, IS_EMPTY, HEAD, TAIL, CONS, DISPLAY, READ_LINE, READ_WORD, READ_INT, - LENGTH, AT, STRCAT, SUBSTR, + LENGTH, AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, STRCAT, SUBSTR, ANNOTATE, TYPEOF, LIST_TYPE, ASSIGN, IF; } diff --git a/compiler/driver.cpp b/compiler/driver.cpp index d1f673d..10975a3 100644 --- a/compiler/driver.cpp +++ b/compiler/driver.cpp @@ -35,7 +35,7 @@ namespace basil { void print_asm(bool should) { _print_asm = should; } - + void compile_only(bool should) { _compile_only = should; } @@ -58,11 +58,17 @@ namespace basil { Value parse(TokenView& view) { vector results; + TokenView end = view; + while (end) end.read(); while (view.peek()) { Value line = parse_line(view, view.peek().column); if (!line.is_void()) results.push(line); } - + if (_print_tokens) { + print(BOLDYELLOW); + while (end) print(end.read(), " "); + println(RESET); + } if (_print_parsed) { print(BOLDGREEN); for (const Value& v : results) println(v); @@ -217,6 +223,9 @@ namespace basil { jasmine::Object object; compile(result, object); + auto code = object.code(); + while (code.size()) printf("%02x ", code.read()); + printf("\n"); add_native_functions(object); object.load(); if (error_count()) return print_errors(_stdout), 1; @@ -232,6 +241,19 @@ namespace basil { return d + e; } + vector instantiations(ref env) { + vector insts; + for (const auto& p : *env) { + if (p.second.value.is_function()) { + const FunctionValue& fn = p.second.value.get_function(); + if (fn.instantiations()) for (auto& i : *fn.instantiations()) { + insts.push(i.second); + } + } + } + return insts; + } + int build(Source& src, const char* filename) { string dest = change_ending(filename, ".o"); auto view = src.begin(); @@ -248,15 +270,32 @@ namespace basil { Value result = eval(global, program); if (error_count()) return print_errors(_stdout), 1; - if (!result.is_runtime()) return 0; - if (_print_ast) - println(BOLDCYAN, result.get_runtime(), RESET, "\n"); + auto insts = instantiations(global); + if (result.is_runtime()) { + if (_print_ast) + println(BOLDCYAN, result.get_runtime(), RESET, "\n"); - jasmine::Object object; - compile(result, object); - if (error_count()) return print_errors(_stdout), 1; + jasmine::Object object; + compile(result, object); + if (error_count()) return print_errors(_stdout), 1; + object.writeELF((const char*)dest.raw()); + } + else if (insts.size() > 0) { + jasmine::Object object; + Function container(string(".") + filename); + for (Value f : insts) f.get_runtime()->emit(container); + if (_print_ir) { + print(BOLDMAGENTA); + print(container); + println(RESET, "\n"); + } + container.allocate(); + container.emit(object); + emit_constants(object); + if (error_count()) return print_errors(_stdout), 1; + object.writeELF((const char*)dest.raw()); + } - object.writeELF((const char*)dest.raw()); return 0; } } \ No newline at end of file diff --git a/compiler/eval.cpp b/compiler/eval.cpp index 4d6472a..571e4b0 100644 --- a/compiler/eval.cpp +++ b/compiler/eval.cpp @@ -215,7 +215,6 @@ namespace basil { Value eval_list(ref env, const Value& list); Value eval(ref env, Value term); - Value infix(ref env, const Value& term, bool is_macro); Value define(ref env, const Value& term, bool is_macro); // utilities @@ -330,7 +329,7 @@ namespace basil { err(rest.loc(), "Infix function must take at least one ", "argument."); return; } - basil::infix(env, item, true); + // basil::infix(env, item, true); } else define(env, item, true); } else @@ -352,27 +351,39 @@ namespace basil { } bool is_keyword(const Value& term) { - return term.is_list() && tail(term).is_list() && tail(tail(term)).is_void() && - symbol_matches(head(term), "quote") && head(tail(term)).is_symbol(); + return term.is_symbol() && !symbol_for(term.get_symbol()).endswith('?'); } - bool is_valid_argument(const Value& term) { + bool is_valid_variable(const Value& term) { return term.is_symbol() // name || is_annotation(term) && tail(term).is_list() && head(tail(term)).is_symbol(); } + bool is_valid_argument(const Value& term) { + return term.is_symbol() && symbol_for(term.get_symbol()).endswith('?') // name + || is_annotation(term) && tail(term).is_list() && head(tail(term)).is_symbol(); + } + bool is_valid_def(const Value& term) { - return is_valid_argument(term) || term.is_list() && head(term).is_symbol() || + return is_valid_argument(term) || term.is_list() && is_keyword(head(term)) || is_annotation(term) && tail(term).is_list() && is_valid_def(head(tail(term))); } bool is_valid_infix_def(const Value& term) { - return term.is_list() && tail(term).is_list() && head(tail(term)).is_symbol() || - is_annotation(term) && tail(term).is_list() && is_valid_infix_def(head(tail(term))); + return term.is_list() && tail(term).is_list() && is_valid_argument(head(term)) && is_keyword(head(tail(term))) + || is_annotation(term) && tail(term).is_list() && is_valid_infix_def(head(tail(term))); + } + + string get_variable_name(const Value& term) { + return is_annotation(term) ? get_variable_name(head(tail(term))) : symbol_for(term.get_symbol()); } string get_arg_name(const Value& term) { - return is_annotation(term) ? get_arg_name(head(tail(term))) : symbol_for(term.get_symbol()); + if (is_annotation(term)) return get_arg_name(head(tail(term))); + else { + const string& name = symbol_for(term.get_symbol()); + return name[{0, name.size() - 1}]; + } } string get_def_name(const Value& term) { @@ -396,51 +407,71 @@ namespace basil { void visit_defs(ref env, const Value& item) { if (!item.is_list()) return; Value h = head(item); - if (symbol_matches(h, "def") || symbol_matches(h, "infix")) { - bool infix = symbol_matches(h, "infix"); + if (symbol_matches(h, "def")) { u8 precedence = 0; + bool has_precedence = false; vector values = to_vector(item); u32 i = 1; - if (values.size() >= 2 && infix && values[i].is_int()) { + if (values.size() >= 2 && values[i].is_int()) { precedence = u8(values[i].get_int()); // consume precedence + has_precedence = true; i++; } - if (i + 1 >= values.size() || !(infix ? is_valid_infix_def(values[i]) : is_valid_def(values[i]))) { - err(item.loc(), "Expected variable or function name ", "in definition."); - return; - } - if (values.size() < 3) err(item.loc(), "Expected value in definition."); - if (is_valid_argument(values[i])) { // variable - string name = get_arg_name(values[i]); + if (is_valid_variable(values[i])) { // variable + string name = get_variable_name(values[i]); + if (values.size() < 3) { + err(item.loc(), "Expected initial value in variable definition."); + return; + } // if (env->find(name)) { // err(values[i].loc(), "Redefinition of '", name, "'."); // return; // } if (!env->find(name)) env->def(name); - } else { // procedure - if (infix) { - Value rest = tail(values[i]); - if (!rest.is_list()) { - err(rest.loc(), "Infix function must take at least one ", "argument."); - return; - } - string name = get_infix_name(values[i]); - // if (env->find(name)) { - // err(values[i].loc(), "Redefinition of '", name, "'."); - // return; - // } - if (!env->find(name)) env->infix(name, to_vector(tail(rest)).size() + 1, precedence); - } else { - const string& name = get_def_name(values[i]); - // if (env->find(name)) { - // err(values[i].loc(), "Redefinition of '", name, "'."); - // return; - // } - if (!env->find(name)) env->def(name, to_vector(tail(values[i])).size()); + } + else if (is_valid_def(values[i])) { // procedure + const string& name = get_def_name(values[i]); + if (name.endswith('?')) { + err(values[i].loc(), "Invalid name for procedure: cannot end in '?'."); + return; + } + if (has_precedence) { + err(values[i - 1].loc(), "Precedence cannot be specified for non-infix procedures."); + return; } + if (values.size() < 3) { + err(item.loc(), "Expected procedure body in procedure definition."); + return; + } + // if (env->find(name)) { + // err(values[i].loc(), "Redefinition of '", name, "'."); + // return; + // } + if (!env->find(name)) env->def(name, to_vector(tail(values[i])).size()); + } + else if (is_valid_infix_def(values[i])) { // infix procedure + Value rest = tail(values[i]); + if (!rest.is_list()) { + err(rest.loc(), "Infix procedure must take at least one ", "argument."); + return; + } + string name = get_infix_name(values[i]); + if (name.endswith('?')) { + err(values[i].loc(), "Invalid name for infix procedure: cannot end in '?'."); + return; + } + if (values.size() < 3) { + err(item.loc(), "Expected procedure body in infix procedure definition."); + return; + } + if (!env->find(name)) env->infix(name, to_vector(tail(rest)).size() + 1, precedence); } - } else - traverse_list(env, item, visit_defs); + else { + err(item.loc(), "Invalid definition."); + return; + } + } + else traverse_list(env, item, visit_defs); } void handle_macro(ref env, Value& item); @@ -702,6 +733,7 @@ namespace basil { values.put(func.type(), func); existing->value = Value(new IntersectValue(values), find(existing->value.type(), func.type())); + return Value(VOID); } else { err(func.loc(), "Cannot redefine symbol '", name, "' of type '", existing->value.type(), "' as function."); @@ -716,31 +748,41 @@ namespace basil { // visit_defs already does some error-checking, so we // don't need to check the number of values or their types // exhaustively. + u8 precedence = 0; - if (is_valid_argument(values[1])) { // variable - string name = get_arg_name(values[1]); + u32 i = 1; + if (values[i].is_int()) { // precedence + precedence = (u8)values[i].get_int(); + i++; + } + + if (is_valid_variable(values[i])) { // variable + string name = get_variable_name(values[i]); if (is_macro) { - if (is_annotation(values[1])) { - err(values[1].loc(), "Type annotations are forbidden in macro definitions."); + if (is_annotation(values[i])) { + err(values[i].loc(), "Type annotations are forbidden in macro definitions."); return error(); } - env->def_macro(name, Value(new AliasValue(values[2]))); + env->def_macro(name, Value(new AliasValue(values[i + 1]))); return Value(VOID); } else { - Value init = eval(env, values[2]); + Value init = eval(env, values[i + 1]); if (env->is_runtime()) init = lower(init); - if (is_annotation(values[1])) init = annotate(init, eval(env, annotation_type(values[1]))); + if (is_annotation(values[i])) init = annotate(init, eval(env, annotation_type(values[i]))); env->def(name, init); if (init.is_runtime()) - return new ASTDefine(values[0].loc(), env, values[1].get_symbol(), init.get_runtime()); + return new ASTDefine(values[0].loc(), env, values[i].get_symbol(), init.get_runtime()); return Value(VOID); } - } else if (values[1].is_list()) { // procedure - string name = get_def_name(values[1]); + } else if (values[i].is_list()) { // procedure + bool infix = !is_valid_def(values[i]); + string name = infix ? get_infix_name(values[i]) : get_def_name(values[i]); ref function_env = newref(env); - vector args = to_vector(tail(get_def_info(values[1]))); + vector args; + vector elts = to_vector(get_def_info(values[i])); + for (u32 i = 0; i < elts.size(); i ++) if (i != infix ? 1 : 0) args.push(elts[i]); vector argnames; vector body; vector argts; @@ -750,10 +792,10 @@ namespace basil { argnames.push(symbol_value(name)); function_env->def(name); } else if (is_keyword(v)) { - argnames.push(eval(env, v).get_symbol() | KEYWORD_ARG_BIT); + argnames.push(v.get_symbol() | KEYWORD_ARG_BIT); } else { err(v.loc(), - "Only symbols, annotated symbols, and quoted symbols " + "Only argument names and keywords " "are permitted within an argument list; given '", v, "'."); return error(); @@ -777,23 +819,24 @@ namespace basil { } else if (!is_keyword(v)) argts.push(ANY); } - for (u32 i = 2; i < values.size(); i++) body.push(values[i]); + for (u32 j = i + 1; j < values.size(); j ++) body.push(values[j]); Value body_term = cons(Value("do"), list_of(body)); if (is_macro) { - if (is_annotation(values[1])) { - err(values[1].loc(), "Type annotations are forbidden in macro definitions."); + if (is_annotation(values[i])) { + err(values[i].loc(), "Type annotations are forbidden in macro definitions."); return error(); } Value mac(new MacroValue(function_env, argnames, body_term)); - env->def_macro(name, mac, argnames.size()); + if (infix) env->infix_macro(name, mac, argnames.size(), precedence); + else env->def_macro(name, mac, argnames.size()); } else { const Type* returntype = ANY; - if (is_annotation(values[1])) { - Value tval = eval(env, annotation_type(values[1])); + if (is_annotation(values[i])) { + Value tval = eval(env, annotation_type(values[i])); if (!tval.is_type()) { if (tval.type()->coerces_to(TYPE)) tval = cast(tval, TYPE); else { - err(values[1].loc(), "Expected type value in annotation, given '", tval.type(), "'."); + err(values[i].loc(), "Expected type value in annotation, given '", tval.type(), "'."); return error(); } } @@ -805,12 +848,18 @@ namespace basil { Def* existing = env->find(name); if (existing && !existing->value.is_void()) { return overload(env, existing, func, name); + } else if (infix) { + env->infix(name, func, argnames.size(), precedence); } else env->def(name, func, argnames.size()); - // if (argst->concrete()) instantiate(func.loc(), func.get_function(), find(argts)); + if (argst->concrete()) { + func.get_function().instantiate(argst, + new ASTIncompleteFn(func.loc(), argst, symbol_value(name))); + instantiate(func.loc(), func.get_function(), find(argts)); + } } return Value(VOID); } else { - err(values[1].loc(), "First parameter to definition must be ", + err(values[i].loc(), "First parameter to definition must be ", "a symbol (for variable or alias) or list (for procedure ", "or macro)."); return error(); } @@ -828,6 +877,8 @@ namespace basil { } Value def_info = get_def_info(values[i]); + bool infix = false; + if (!is_valid_def(def_info)) infix = false; const string& name = symbol_for(head(tail(def_info)).get_symbol()); vector args; @@ -1201,12 +1252,12 @@ namespace basil { if (name == "quote") return head(tail(term)).clone(); else if (name == "def") return define(env, term, false); - else if (name == "infix") - return infix(env, term, false); + // else if (name == "infix") + // return infix(env, term, false); else if (name == "macro") return define(env, term, true); - else if (name == "infix-macro") - return infix(env, term, true); + // else if (name == "infix-macro") + // return infix(env, term, true); else if (name == "lambda") return lambda(env, term); else if (name == "do") @@ -1229,7 +1280,7 @@ namespace basil { if (h.is_symbol() && tail(term).is_void()) { const Def* def = env->find(symbol_for(h.get_symbol())); - if (def->is_infix) return first; + if (def && def->is_infix) return first; } if (first.is_macro()) { diff --git a/compiler/ir.cpp b/compiler/ir.cpp index ebca315..ebdb0c7 100644 --- a/compiler/ir.cpp +++ b/compiler/ir.cpp @@ -190,7 +190,7 @@ namespace basil { bool changed = true; while (changed) { changed = false; - for (i64 i = _insns.size() - 1; i >= 0; i --) { + for (i64 i = i64(_insns.size()) - 1; i >= 0; i --) { Insn* in = _insns[i]; u32 initial_in = in->in().size(), initial_out = in->out().size(); for (const Insn* succ : in->succ()) { @@ -331,6 +331,11 @@ namespace basil { open_frame(_stack); for (Insn* i : _insns) i->emit(); local_label(all_labels[_end]); + if (_fns.size() > 0) { // hack + mov(x64::r64(x64::RAX), x64::imm64(60)); + mov(x64::r64(x64::RDI), x64::imm64(0)); + syscall(); + } close_frame(_stack); } diff --git a/compiler/lex.cpp b/compiler/lex.cpp index 3851d71..3e9201a 100644 --- a/compiler/lex.cpp +++ b/compiler/lex.cpp @@ -3,6 +3,7 @@ #include "errors.h" #include "ctype.h" #include "stdlib.h" +#include "driver.h" namespace basil { Token::Token(TokenType type_in, const const_slice& value_in, u32 line_in, u32 column_in): @@ -13,8 +14,8 @@ namespace basil { } static const char* TOKEN_NAMES[NUM_TOKEN_TYPES] = { - "none", "int", "symbol", "string", "coeff", "left paren", "right paren", - "left bracket", "right bracket", "left brace", "right brace", + "none", "int", "symbol", "string", "coeff", "float", "left paren", "right paren", + "access bracket", "left bracket", "right bracket", "left brace", "right brace", "semicolon", "dot", "comma", "colon", "pipe", "plus", "minus", "quote", "newline" }; @@ -59,7 +60,7 @@ namespace basil { return issymbolstart(ch) && !isalpha(ch); } - Token scan(Source::View& view) { + Token scan(Source::View& view, bool follows_space) { const u8* start = view.pos(); u32 start_col = view.col(), line = view.line(); u8 ch = view.peek(); @@ -99,7 +100,9 @@ namespace basil { } else if (isdelimiter(ch)) { view.read(); - return Token(DELIMITERS[ch], { 1, start }, line, start_col); + TokenType type = DELIMITERS[ch]; + if (DELIMITERS[ch] == T_LBRACK && !follows_space) type = T_ACCESS; + return Token(type, { 1, start }, line, start_col); } else if (issymbolstart(ch)) { view.read(); @@ -125,7 +128,7 @@ namespace basil { } else if (isspace(ch)) { view.read(); - return scan(view); + return scan(view, true); } else err({ line, u16(view.col()) }, "Unexpected character in input '", view.read(), "'."); return NONE; @@ -163,8 +166,6 @@ namespace basil { while (view.peek()) _tokens->push(scan(view)); - if (error_count()) - print_errors(_stdout, *_source), clear_errors(); } } diff --git a/compiler/lex.h b/compiler/lex.h index 2239714..8e7896a 100644 --- a/compiler/lex.h +++ b/compiler/lex.h @@ -7,8 +7,8 @@ namespace basil { enum TokenType : u8 { T_NONE, - T_INT, T_SYMBOL, T_STRING, T_COEFF, - T_LPAREN, T_RPAREN, T_LBRACK, T_RBRACK, T_LBRACE, T_RBRACE, + T_INT, T_SYMBOL, T_STRING, T_COEFF, T_FLOAT, + T_LPAREN, T_RPAREN, T_ACCESS, T_LBRACK, T_RBRACK, T_LBRACE, T_RBRACE, T_SEMI, T_DOT, T_COMMA, T_COLON, T_PIPE, T_PLUS, T_MINUS, T_QUOTE, T_NEWLINE, @@ -41,7 +41,7 @@ namespace basil { void expand(); }; - Token scan(Source::View& view); + Token scan(Source::View& view, bool follows_space = false); } void write(stream& io, const basil::Token& t); diff --git a/compiler/parse.cpp b/compiler/parse.cpp index ebe019a..fe9af6f 100644 --- a/compiler/parse.cpp +++ b/compiler/parse.cpp @@ -2,294 +2,282 @@ #include "errors.h" namespace basil { - i64 parse_int(const string& s) { - static buffer b; - write(b, s); - i64 i; - read(b, i); - return i; - } + i64 parse_int(const string& s) { + static buffer b; + write(b, s); + i64 i; + read(b, i); + return i; + } - string parse_string(const string& s) { - string t; - const u8* sptr = s.raw(); - sptr ++; // skip initial quote - while (*sptr && *sptr != '\"') { - if (*sptr == '\\') { - sptr ++; - switch (*sptr) { - case 'a': t += '\a'; break; - case 'b': t += '\b'; break; - case 'f': t += '\f'; break; - case 'n': t += '\n'; break; - case 'r': t += '\r'; break; - case 't': t += '\t'; break; - case 'v': t += '\v'; break; - case '0': t += '\0'; break; - case '"': t += '"'; break; - case '\'': t += '\''; break; - case '\\': t += '\\'; break; - case '?': t += '\?'; break; - case '\0': break; - } - sptr ++; - } - t += *(sptr ++); - } - sptr ++; // skip final quote - return t; - } + string parse_string(const string& s) { + string t; + const u8* sptr = s.raw(); + sptr++; // skip initial quote + while (*sptr && *sptr != '\"') { + if (*sptr == '\\') { + sptr++; + switch (*sptr) { + case 'a': t += '\a'; break; + case 'b': t += '\b'; break; + case 'f': t += '\f'; break; + case 'n': t += '\n'; break; + case 'r': t += '\r'; break; + case 't': t += '\t'; break; + case 'v': t += '\v'; break; + case '0': t += '\0'; break; + case '"': t += '"'; break; + case '\'': t += '\''; break; + case '\\': t += '\\'; break; + case '?': t += '\?'; break; + case '\0': break; + } + sptr++; + } + t += *(sptr++); + } + sptr++; // skip final quote + return t; + } - Value parse_primary(TokenView& view, u32 indent); - void parse_line(TokenView& view, u32 indent, bool consume_line, - vector& terms); + Value parse_primary(TokenView& view, u32 indent); + void parse_line(TokenView& view, u32 indent, bool consume_line, vector& terms); - bool out_of_input(TokenView& view) { // returns true if out of input - if (view.repl()) { - view.expand(); - return false; + bool out_of_input(TokenView& view) { // returns true if out of input + if (view.repl()) { + view.expand(); + return error_count() != 0; // if there are errors, act like out of input + } else { + err(view.peek(), "Unexpected end of input."); + return true; + } } - else { - err(view.peek(), "Unexpected end of input."); - return true; + + void parse_enclosed(TokenView& view, vector& terms, TokenType terminator, u32 indent) { + vector tuple_elements; + bool errored = false; + while (view.peek().type != terminator) { + while (view.peek().type == T_NEWLINE) view.read(); + if (!view.peek() && out_of_input(view)) return; + terms.push(parse(view, indent)); + if (view.peek().type == T_COMMA) { + if (terminator != T_RPAREN && !errored) { // not enclosed by parens + err(view.peek(), "Tuple literals require parentheses."), errored = true; + tuple_elements.push(error()); + } else if (!errored) { // correct usage + tuple_elements.push(list_of(terms)); + terms.clear(); + } + view.read(); + } + } + view.read(); + if (tuple_elements.size() > 0) { + tuple_elements.push(list_of(terms)); + terms.clear(); + terms.push(Value("tuple-of")); + for (const Value& v : tuple_elements) terms.push(v); + } } - } - void parse_enclosed(TokenView& view, vector& terms, - TokenType terminator, u32 indent) { - vector tuple_elements; - bool errored = false; - while (view.peek().type != terminator) { - while (view.peek().type == T_NEWLINE) view.read(); - if (!view.peek() && out_of_input(view)) return; - terms.push(parse(view, indent)); - if (view.peek().type == T_COMMA) { - if (terminator != T_RPAREN && !errored) { // not enclosed by parens - err(view.peek(), "Tuple literals require parentheses."), errored = true; - tuple_elements.push(error()); + void parse_block(TokenView& view, vector& terms, u32 prev_indent, u32 indent) { + while (view.peek().column > prev_indent) { + if (view.peek().type != T_NEWLINE) terms.push(parse_line(view, indent, false)); + if (view.peek().type == T_NEWLINE) { + if (view.peek().column <= prev_indent && view.repl()) { + view.read(); + return; + } else + view.read(); + } + if (!view.peek() && (!view.repl() || out_of_input(view))) return; } - else if (!errored) { // correct usage - tuple_elements.push(list_of(terms)); - terms.clear(); + if (view.peek().type == T_QUOTE && view.peek().column == prev_indent) { // continuation + u32 new_indent = view.peek().column; + parse_line(view, new_indent, true, terms); } - view.read(); - } } - view.read(); - if (tuple_elements.size() > 0) { - tuple_elements.push(list_of(terms)); - terms.clear(); - terms.push(Value("tuple-of")); - for (const Value& v : tuple_elements) terms.push(v); + + Value apply_op(TokenView& view, Value lhs, Value rhs, TokenType op) { + switch (op) { + case T_COLON: { + Value v = list_of(Value("annotate"), lhs, rhs); + v.set_location(lhs.loc()); + return v; + } + case T_DOT: { + Value v = list_of(Value("at", SYMBOL), lhs, list_of(Value("quote", SYMBOL), rhs)); + v.set_location(lhs.loc()); + return v; + } + default: + err(view.peek(), "Unexpected binary operator '", view.peek().value, "'."); + view.read(); + return Value(ERROR); + } } - } - void parse_block(TokenView& view, vector& terms, - u32 prev_indent, u32 indent) { - while (view.peek().column > prev_indent) { - if (view.peek().type != T_NEWLINE) - terms.push(parse_line(view, indent, false)); - if (view.peek().type == T_NEWLINE) { - if (view.peek().column <= prev_indent && view.repl()) { - view.read(); - return; - } - else view.read(); - } - if (!view.peek() && (!view.repl() || out_of_input(view))) return; + Value parse_binary(TokenView& view, Value lhs, TokenType prev_op, u32 indent) { + Value rhs = parse_primary(view, indent); + TokenType next_op = view.peek().type; + if (next_op == T_COLON || next_op == T_DOT) { + view.read(); + if (next_op == T_COLON && prev_op == T_DOT) // : has higher + // precedence + return apply_op(view, lhs, parse_binary(view, rhs, next_op, indent), prev_op); + else + return parse_binary(view, apply_op(view, lhs, rhs, prev_op), next_op, indent); + } + return apply_op(view, lhs, rhs, prev_op); } - if (view.peek().type == T_QUOTE - && view.peek().column == prev_indent) { // continuation - u32 new_indent = view.peek().column; - parse_line(view, new_indent, true, terms); - } - } - Value apply_op(TokenView& view, Value lhs, Value rhs, TokenType op) { - switch (op) { - case T_COLON: { - Value v = list_of(Value("annotate"), lhs, rhs); - v.set_location(lhs.loc()); + Value parse_primary(TokenView& view, u32 indent) { + SourceLocation first = view.peek(); + Value v; + switch (view.peek().type) { + case T_INT: { + v = parse_int(view.read().value); + v.set_location(first); + break; + } + case T_SYMBOL: { + v = Value(string(view.read().value), SYMBOL); + v.set_location(first); + break; + } + case T_STRING: { + v = Value(parse_string(view.read().value), STRING); + v.set_location(first); + break; + } + case T_PLUS: { + view.read(); + v = list_of(Value("+"), 0, parse_primary(view, indent)); + v.set_location(first); + break; + } + case T_MINUS: { + view.read(); + v = list_of(Value("-"), 0, parse_primary(view, indent)); + v.set_location(first); + break; + } + case T_QUOTE: { + view.read(); + v = list_of(Value("quote"), parse_primary(view, indent)); + v.set_location(first); + break; + } + case T_COEFF: { + i64 i = parse_int(view.read().value); + v = list_of(Value("*"), i, parse_primary(view, indent)); + v.set_location(first); + break; + } + case T_LPAREN: { + view.read(); + vector terms; + parse_enclosed(view, terms, T_RPAREN, indent); + for (const Value& v : terms) + if (v.is_error()) return error(); + v = list_of(terms); + v.set_location(first); + break; + } + case T_LBRACK: { + view.read(); + vector terms; + terms.push(Value("list-of")); + parse_enclosed(view, terms, T_RBRACK, indent); + for (const Value& v : terms) + if (v.is_error()) return error(); + v = list_of(terms); + v.set_location(first); + break; + } + case T_LBRACE: { + view.read(); + vector terms; + terms.push(Value("set-of")); + parse_enclosed(view, terms, T_RBRACE, indent); + for (const Value& v : terms) + if (v.is_error()) return error(); + v = list_of(terms); + v.set_location(first); + break; + } + case T_PIPE: { + view.read(); + vector terms; + parse_enclosed(view, terms, T_PIPE, indent); + for (const Value& v : terms) + if (v.is_error()) return error(); + v = cons(Value("splice"), list_of(terms)); + v.set_location(first); + break; + } + default: + err(view.peek(), "Unexpected token '", escape(view.peek().value), "'."); + view.read(); + return error(); + } + while (view.peek().type == T_ACCESS) { + view.read(); + vector terms; + terms.push(Value("list-of")); + parse_enclosed(view, terms, T_RBRACK, indent); + for (const Value& v : terms) + if (v.is_error()) return error(); + Value indices = terms.size() == 2 ? terms[1] : list_of(terms); + v = list_of(Value("at", SYMBOL), v, indices); + v.set_location(first); + } return v; - } - case T_DOT: { - Value v = list_of(lhs, rhs); - v.set_location(lhs.loc()); + } + + Value parse(TokenView& view, u32 indent) { + Value v = parse_primary(view, indent); + if (v.is_error()) return v; + if (view.peek().type == T_DOT) view.read(), v = parse_binary(view, v, T_DOT, indent); + if (view.peek().type == T_COLON) { + view.read(); + // parse a labeled block + v = parse_binary(view, v, T_COLON, indent); + } return v; - } - default: - err(view.peek(), "Unexpected binary operator '", - view.peek().value, "'."); - view.read(); - return Value(ERROR); } - } - Value parse_binary(TokenView& view, Value lhs, TokenType prev_op, - u32 indent) { - Value rhs = parse_primary(view, indent); - TokenType next_op = view.peek().type; - if (next_op == T_COLON || next_op == T_DOT) { - view.read(); - if (next_op == T_COLON && prev_op == T_DOT) // : has higher - // precedence - return apply_op(view, lhs, - parse_binary(view, rhs, next_op, indent), prev_op); - else - return parse_binary(view, - apply_op(view, lhs, rhs, prev_op), next_op, indent); + void parse_line(TokenView& view, u32 indent, bool consume_line, vector& terms) { + SourceLocation first = view.peek(); + while (view.peek()) { + if (view.peek().type == T_NEWLINE) { + view.read(); + if (!view.peek() && (!view.repl() || out_of_input(view))) return; + if (view.peek().column > indent) parse_block(view, terms, indent, view.peek().column); + else if (view.peek().type == T_QUOTE && view.peek().column == indent) { // continuation + u32 new_indent = view.peek().column; + parse_line(view, new_indent, true, terms); + } else if (!consume_line) + view.rewind(); + + return; + } + Value v = parse(view, indent); + if (!v.is_error()) terms.push(v); + } } - return apply_op(view, lhs, rhs, prev_op); - } - Value parse_primary(TokenView& view, u32 indent) { - SourceLocation first = view.peek(); - switch (view.peek().type) { - case T_INT: { - Value v(parse_int(view.read().value)); - v.set_location(first); - return v; - } - case T_SYMBOL: { - Value v(string(view.read().value)); - v.set_location(first); - return v; - } - case T_STRING: { - Value v(parse_string(view.read().value), STRING); - v.set_location(first); - return v; - } - case T_PLUS: { - view.read(); - Value v = list_of(Value("+"), 0, parse_primary(view, indent)); - v.set_location(first); - return v; - } - case T_MINUS: { - view.read(); - Value v = list_of(Value("-"), 0, parse_primary(view, indent)); - v.set_location(first); - return v; - } - case T_QUOTE: { - view.read(); - Value v = list_of(Value("quote"), parse_primary(view, indent)); - v.set_location(first); - return v; - } - case T_COEFF: { - i64 i = parse_int(view.read().value); - Value v = list_of(Value("*"), i, parse_primary(view, indent)); - v.set_location(first); - return v; - } - case T_LPAREN: { - view.read(); - vector terms; - parse_enclosed(view, terms, T_RPAREN, indent); - for (const Value& v : terms) if (v.is_error()) return error(); - Value v = list_of(terms); - v.set_location(first); - return v; - } - case T_LBRACK: { - view.read(); - vector terms; - terms.push(Value("list-of")); - parse_enclosed(view, terms, T_RBRACK, indent); - for (const Value& v : terms) if (v.is_error()) return error(); - Value v = list_of(terms); - v.set_location(first); - return v; - } - case T_LBRACE: { - view.read(); + Value parse_line(TokenView& view, u32 indent, bool consume_line) { + SourceLocation first = view.peek(); vector terms; - terms.push(Value("set-of")); - parse_enclosed(view, terms, T_RBRACE, indent); - for (const Value& v : terms) if (v.is_error()) return error(); + if (view.peek().type == T_NEWLINE) { + if (consume_line) view.read(); + return empty(); + } + parse_line(view, indent, consume_line, terms); + Value v = list_of(terms); v.set_location(first); return v; - } - case T_PIPE: { - view.read(); - vector terms; - parse_enclosed(view, terms, T_PIPE, indent); - for (const Value& v : terms) if (v.is_error()) return error(); - Value v = cons(Value("splice"), list_of(terms)); - v.set_location(first); - return v; - } - default: - err(view.peek(), "Unexpected token '", - escape(view.peek().value), "'."); - view.read(); - return error(); } - } - - Value parse(TokenView& view, u32 indent) { - Value v = parse_primary(view, indent); - if (v.is_error()) return v; - if (view.peek().type == T_DOT) - view.read(), v = parse_binary(view, v, T_DOT, indent); - if (view.peek().type == T_COLON) { - view.read(); - // parse a labeled block - if (v.is_symbol() && view.peek().type == T_NEWLINE) { - view.read(); - vector terms; - terms.push(v); // label; - if (!view.peek() && out_of_input(view)) return error(); - if (view.peek().column > indent) - parse_block(view, terms, indent, view.peek().column); - return list_of(terms); - } - else if (view.peek().type == T_NEWLINE) { - view.rewind(); // unget : - return v; // unlabeled block, handled in parse_line - } - else v = parse_binary(view, v, T_COLON, indent); - } - return v; - } - - void parse_line(TokenView& view, u32 indent, bool consume_line, - vector& terms) { - SourceLocation first = view.peek(); - while (view.peek()) { - if (view.peek().type == T_NEWLINE) { - view.read(); - if (!view.peek() && (!view.repl() || out_of_input(view))) return; - if (view.peek().column > indent) - parse_block(view, terms, indent, view.peek().column); - else if (view.peek().type == T_QUOTE - && view.peek().column == indent) { // continuation - u32 new_indent = view.peek().column; - parse_line(view, new_indent, true, terms); - } - else if (!consume_line) view.rewind(); - - return; - } - Value v = parse(view, indent); - if (!v.is_error()) terms.push(v); - } - } - - Value parse_line(TokenView& view, u32 indent, bool consume_line) { - SourceLocation first = view.peek(); - vector terms; - if (view.peek().type == T_NEWLINE) { - if (consume_line) view.read(); - return empty(); - } - parse_line(view, indent, consume_line, terms); - - Value v = list_of(terms); - v.set_location(first); - return v; - } -} \ No newline at end of file +} // namespace basil \ No newline at end of file diff --git a/compiler/type.cpp b/compiler/type.cpp index 4573228..e432ff8 100644 --- a/compiler/type.cpp +++ b/compiler/type.cpp @@ -19,6 +19,8 @@ namespace basil { bool Type::coerces_to(const Type* other) const { return other == this || other == ANY + || other->kind() == KIND_RUNTIME && this->coerces_to(((const RuntimeType*)other)->base()) + || this == VOID && other->kind() == KIND_LIST || other->kind() == KIND_SUM && ((const SumType*)other)->has(this); } @@ -75,6 +77,38 @@ namespace basil { write(io, floating() ? "f" : "i", size()); } + // NamedType + + NamedType::NamedType(const string& name, const Type* base): + Type(1921110990418496011ul ^ ::hash(name) ^ base->hash()), + _name(name), _base(base) {} + + const string& NamedType::name() const { + return _name; + } + + const Type* NamedType::base() const { + return _base; + } + + TypeKind NamedType::kind() const { + return KIND_NAMED; + } + + bool NamedType::operator==(const Type& other) const { + return other.kind() == KIND_NAMED + && ((const NamedType&)other)._base == _base + && ((const NamedType&)other)._name == _name; + } + + bool NamedType::coerces_to(const Type* other) const { + return Type::coerces_to(other) || other == _base; + } + + void NamedType::format(stream& io) const { + write(io, _name); + } + // ListType ListType::ListType(const Type* element): @@ -429,6 +463,11 @@ namespace basil { *((const RuntimeType&)other).base() == *base(); } + bool RuntimeType::coerces_to(const Type* other) const { + return Type::coerces_to(other) + || other->kind() == KIND_RUNTIME && base()->coerces_to(((const RuntimeType*)other)->base()); + } + void RuntimeType::format(stream& io) const { write(io, "runtime<", _base, ">"); } @@ -482,6 +521,10 @@ namespace basil { return other.kind() == kind() && _id == ((const TypeVariable&)other)._id; } + bool TypeVariable::coerces_to(const Type* other) const { + return Type::coerces_to(other) || !concrete() || actual()->coerces_to(other); + } + void TypeVariable::format(stream& io) const { write(io, typevar_names[_id]); if (type_variables[_id]->concrete()) diff --git a/compiler/type.h b/compiler/type.h index 5ef03f1..7d34404 100644 --- a/compiler/type.h +++ b/compiler/type.h @@ -22,7 +22,8 @@ namespace basil { KIND_FUNCTION = GC_KIND_FLAG | 5, KIND_ALIAS = GC_KIND_FLAG | 6, KIND_MACRO = GC_KIND_FLAG | 7, - KIND_RUNTIME = GC_KIND_FLAG | 8 + KIND_RUNTIME = GC_KIND_FLAG | 8, + KIND_NAMED = GC_KIND_FLAG | 9 }; class Type { @@ -64,6 +65,20 @@ namespace basil { void format(stream& io) const override; }; + class NamedType : public Type { + string _name; + const Type* _base; + public: + NamedType(const string& name, const Type* base); + + const string& name() const; + const Type* base() const; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + bool coerces_to(const Type* other) const override; + void format(stream& io) const override; + }; + class ListType : public Type { const Type* _element; public: @@ -186,6 +201,7 @@ namespace basil { const Type* base() const; TypeKind kind() const override; bool operator==(const Type& other) const override; + bool coerces_to(const Type* other) const override; void format(stream& io) const override; }; @@ -203,6 +219,7 @@ namespace basil { const Type* concretify() const override; TypeKind kind() const override; bool operator==(const Type& other) const override; + bool coerces_to(const Type* other) const override; void format(stream& io) const override; }; diff --git a/compiler/values.cpp b/compiler/values.cpp index 5f29a58..48ad46c 100644 --- a/compiler/values.cpp +++ b/compiler/values.cpp @@ -65,7 +65,13 @@ namespace basil { Value::Value(ArrayValue* a) { set ts; for (const Value& v : *a) ts.insert(v.type()); - _type = find(find(ts), a->size()); + if (ts.size() == 0) ts.insert(ANY); + _type = find(ts.size() > 1 ? find(ts) : *ts.begin(), a->size()); + _data.rc = a; + } + + Value::Value(ArrayValue* a, const Type* t) { + _type = t; _data.rc = a; } @@ -87,7 +93,8 @@ namespace basil { _data.rc = m; } - Value::Value(ASTNode* n) : _type(find(n->type())) { + Value::Value(ASTNode* n) : + _type(n->type()->kind() == KIND_RUNTIME ? n->type() : find(n->type())) { _data.rc = n; } @@ -340,7 +347,7 @@ namespace basil { } write(io, ")"); } else if (is_function()) - write(io, "<#procedure>"); + write(io, "<#", get_function().name() < 0 ? "procedure" : symbol_for(get_function().name()), ">"); else if (is_alias()) write(io, "<#alias>"); else if (is_macro()) @@ -741,6 +748,10 @@ namespace basil { return nullptr; } + const map* FunctionValue::instantiations() const { + return _insts; + } + void FunctionValue::instantiate(const Type* type, ASTNode* body) { if (!_insts) _insts = new map(); auto it = _insts->find(type); @@ -1182,6 +1193,16 @@ namespace basil { Value cast(const Value& val, const Type* type) { if (val.type() == type || type == ANY) return val; + if (val.type()->kind() == KIND_TYPEVAR) { + unify(val.type(), type); + return val; + } + + if (type->kind() == KIND_RUNTIME) { + // we don't lower for ANY, so that generic builtins can do custom things + return ((const RuntimeType*)type)->base() == ANY ? val : lower(val); + } + if (val.is_product()) { if (type == TYPE) { vector ts; @@ -1190,6 +1211,11 @@ namespace basil { } } + if (val.type()->kind() == KIND_ARRAY && type->kind() == KIND_ARRAY + && ((const ArrayType*)val.type())->fixed() && !((const ArrayType*)type)->fixed()) { + return Value(new ArrayValue(val.get_array()), type); + } + if (val.is_list() && type == TYPE) { if (length(val) != Value(1l)) { err(val.loc(), "Only single-element lists can be treated as types."); @@ -1206,7 +1232,7 @@ namespace basil { if (type->kind() == KIND_SUM) { return Value(new SumValue(val), val.type()); } - err(val.loc(), "Could not convert value to type '", type, "'."); + err(val.loc(), "Could not convert value of type '", val.type(), "' to type '", type, "'."); return error(); } @@ -1300,7 +1326,18 @@ namespace basil { } } + Value cast_args(ref env, Value& args, const Type* argst) { + + } + Value call(ref env, Value& callable, const Value& args) { + if (!args.is_product()) { + err(args.loc(), "Expected product value for arguments."); + return error(); + } + for (u32 i = 0; i < args.get_product().size(); i ++) { + if (args.get_product()[i].is_error()) return error(); + } Value function = callable; if (function.is_intersect()) { map ftypes; @@ -1363,24 +1400,54 @@ namespace basil { return error(); } - // Handle runtime arguments. + bool has_runtime = false; + for (u32 i = 0; i < argst->count(); i ++) { + if (args.get_product()[i].is_runtime()) { + has_runtime = true; + break; + } + } + FunctionValue& fn = function.get_function(); + if (fn.is_builtin()) { + if (fn.get_builtin().runtime_only()) has_runtime = true; + } + else { + if (!fn.found_calls()) { + set visited; + find_calls(fn, env, fn.body(), visited); + } + if (fn.recursive()) has_runtime = true; + } + + const ProductType* rtargst = argst; + if (has_runtime) { + vector argts; + for (u32 i = 0; i < argst->count(); i ++) { + if (argst->member(i)->kind() != KIND_RUNTIME) argts.push(find(argst->member(i))); + else argts.push(argst->member(i)); + } + rtargst = (const ProductType*)find(argts); + } Value args_copy = args; for (u32 i = 0; i < argst->count(); i++) { const Value& arg = args.get_product()[i]; const Type* argt = arg.type(); - if (!argt->coerces_to(argst->member(i))) { + if (!argt->coerces_to(rtargst->member(i))) { err(arg.loc(), "Incorrectly typed argument for function '", function, "' at position ", i, - ": expected '", argst->member(i), "', given '", argt, "'."); + ": expected '", rtargst->member(i), "', given '", argt, "'."); return error(); } - if (argt != argst->member(i)) args_copy.get_product()[i] = cast(args.get_product()[i], argst->member(i)); + if (argt != rtargst->member(i)) args_copy.get_product()[i] = cast(args.get_product()[i], rtargst->member(i)); } - FunctionValue& fn = function.get_function(); if (fn.is_builtin()) { - return fn.get_builtin().eval(env, args_copy); + if (has_runtime && fn.get_builtin().should_lower()) + for (Value& v : args_copy.get_product()) v = lower(v); + return has_runtime ? fn.get_builtin().compile(env, args_copy) + : fn.get_builtin().eval(env, args_copy); } else { + vector rtargs; ref fnenv = fn.get_env(); for (u32 i = 0; i < fn.arity(); i++) { if (fn.args()[i] & KEYWORD_ARG_BIT) { @@ -1393,12 +1460,29 @@ namespace basil { } } else { const string& argname = symbol_for(fn.args()[i] & ARG_NAME_MASK); - fnenv->find(argname)->value = args_copy.get_product()[i]; + if (has_runtime) { + Value v = lower(args_copy.get_product()[i]); + ASTNode* n = v.get_runtime(); + n->inc(); + rtargs.push(n); + } + else fnenv->find(argname)->value = args_copy.get_product()[i]; } } - Value prepped = fn.body().clone(); - prep(fnenv, prepped); - return eval(fnenv, prepped); + if (has_runtime) { + ASTNode* body = fn.instantiation(argst); + if (!body) { + fn.instantiate(argst, new ASTIncompleteFn(function.loc(), argst, fn.name())); + body = instantiate(function.loc(), fn, argst); + } + if (!body) return error(); + return new ASTCall(callable.loc(), body, rtargs); + } + else { + Value prepped = fn.body().clone(); + prep(fnenv, prepped); + return eval(fnenv, prepped); + } } } @@ -1502,6 +1586,7 @@ namespace basil { } } } + const Type* argt = find(argts); ASTNode* body = fn.instantiation(argt); if (!body) { diff --git a/compiler/values.h b/compiler/values.h index 4556ab7..2dea420 100644 --- a/compiler/values.h +++ b/compiler/values.h @@ -50,6 +50,7 @@ namespace basil { Value(IntersectValue* i, const Type* type); Value(ProductValue* p); Value(ArrayValue* a); + Value(ArrayValue* a, const Type* type); Value(ref env, const Builtin& b); Value(FunctionValue* f, const Type* ftype); Value(AliasValue* f); @@ -231,6 +232,7 @@ namespace basil { bool recursive() const; void add_call(const FunctionValue* other); ASTNode* instantiation(const Type* args) const; + const map* instantiations() const; void instantiate(const Type* args, ASTNode* body); }; diff --git a/example/arrays.bl b/example/arrays.bl new file mode 100644 index 0000000..4ee3ae6 --- /dev/null +++ b/example/arrays.bl @@ -0,0 +1,2 @@ +$print-tokens true +a[0] \ No newline at end of file diff --git a/example/fibonacci.bl b/example/fibonacci.bl index b92b633..fa3c4d9 100644 --- a/example/fibonacci.bl +++ b/example/fibonacci.bl @@ -1,5 +1,4 @@ -def (fib n) +def (fib n?: int): int if n < 2 n :else (fib n - 1) + (fib n - 2) -display (fib 45) diff --git a/example/fibonacci.c b/example/fibonacci.c index da6919c..4b4d001 100644 --- a/example/fibonacci.c +++ b/example/fibonacci.c @@ -1,8 +1,11 @@ +#include "stdio.h" + int fibonacci(int n) { if (n < 2) return n; else return fibonacci(n - 1) + fibonacci(n - 2); } int main(int argc, char** argv) { - return fibonacci(40); + printf("%d\n", fibonacci(45)); } + diff --git a/example/fibonaccic b/example/fibonaccic deleted file mode 100644 index 2e25d676dbc090e2ebccbf6359d23e30f64c01ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9248 zcmeHN&ubG=5S~qH6MwBy1OpXWe;^fcP1S>XC|g2}f(W$)ic-QR+1ebkDai}8dZ-W( zJQVsT_$PSqt|0ygp1l>6q9WexVV&9cCfP`<5)WSH!P{?U-Zwk@zL!(ptk2%Pm9i}0 zk%DQ6y_^?^^b^22!wdiquERJ!vyh3oV}m8MI78!0^J+%OX)=ZPk!gG|^%$>d8Za`C z^5S}qAvm5k3@(^3Bj-^L%}w(?o0bT4a+p zrF&&a0lcAD8mA0VKok%KL;+Di6c7bO0Z~8{5Cud5QD9UBz8^XD#SDyMj z|9oezw_~kmHpVVKc)0j><4M%pPJR3kMUnMxdu!BMk|zp?0-}H@APR^AqJStM3Wx%t zfG8jehywpdf#asE_4qzmt2t}pP$o-vyyyMXWK zle9G5igUj}Vj)ep5mj5`GFxFw1@5D;>#nq0D?wYWfvbX51#YlhS!(;u0Nh%u*$hH8*vr3I6PTRA ziAuzS&y)Kw91~~AvSH9gq_gVxQt-K(L+ba37(Q{6gVrg=_4~gMN3;5UBHG2Cmf# diff --git a/jasmine/obj.cpp b/jasmine/obj.cpp index 55327fa..23be3ac 100644 --- a/jasmine/obj.cpp +++ b/jasmine/obj.cpp @@ -326,6 +326,52 @@ namespace jasmine { } } + void Object::resolve_ELF_addends() { + auto size = buf.size(); + u8* rawtext = new u8[size]; + u32 i = 0; + while (buf.size()) rawtext[i ++] = buf.read(); + + for (auto& ref : refs) { + u8* pos = (u8*)rawtext + ref.first; + u8* field = pos + ref.second.field_offset; + switch (ref.second.type) { + case ABS8: + case REL8: + *(i8*)field = i8(ref.second.field_offset); + break; + case ABS16_LE: + case REL16_LE: + *(i16*)field = little_endian(ref.second.field_offset); + break; + case ABS16_BE: + case REL16_BE: + *(i16*)field = big_endian(ref.second.field_offset); + break; + case ABS32_LE: + case REL32_LE: + *(i32*)field = little_endian(ref.second.field_offset); + break; + case ABS32_BE: + case REL32_BE: + *(i32*)field = big_endian(ref.second.field_offset); + break; + case ABS64_LE: + case REL64_LE: + *(i64*)field = little_endian(ref.second.field_offset); + break; + case ABS64_BE: + case REL64_BE: + *(i64*)field = big_endian(ref.second.field_offset); + break; + default: + break; + } + } + for (u32 i = 0; i < size; i ++) buf.write(rawtext[i]); + delete[] rawtext; + } + void Object::writeELF(const char* path) { FILE* file = fopen(path, "w"); if (!file) { @@ -373,7 +419,7 @@ namespace jasmine { for (auto& entry : locals) total.push(entry); for (auto& entry : globals) total.push(entry); for (u32 i = 0; i < total.size(); i ++) - sym_indices[total[i].first] = i; + sym_indices[total[i].first] = i + 1; // skip reserved symbol 0 for (auto& entry : total) { u32 ind = strtab.size(); strtab.write(name(entry.first), strlen(name(entry.first)) + 1); @@ -388,6 +434,7 @@ namespace jasmine { symtab.write(8); // symbol size = word size? } + resolve_ELF_addends(); byte_buffer rel; for (auto& entry : refs) { u64 sym = entry.first; diff --git a/jasmine/obj.h b/jasmine/obj.h index a87c3f5..15c0e47 100644 --- a/jasmine/obj.h +++ b/jasmine/obj.h @@ -27,6 +27,7 @@ namespace jasmine { void* loaded_code; void resolve_refs(); + void resolve_ELF_addends(); public: Object(Architecture architecture = DEFAULT_ARCH); Object(const char* path, Architecture architecture = DEFAULT_ARCH); diff --git a/jasmine/x64.h b/jasmine/x64.h index 9b316fe..0dbb15d 100644 --- a/jasmine/x64.h +++ b/jasmine/x64.h @@ -237,7 +237,7 @@ namespace x64 { void jmp(const Arg& dest, Size size = AUTO); void jcc(const Arg& dest, Condition condition); void call(const Arg& dest, Size size = AUTO); - void setcc(const Arg& dest, Condition condition, Size size = AUTO); + void setcc(const Arg& dest, Condition condition, Size size = AUTO); } #endif \ No newline at end of file diff --git a/runtime.cpp b/runtime.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/core.cpp b/runtime/core.cpp similarity index 67% rename from core.cpp rename to runtime/core.cpp index 6663ccf..7955e93 100644 --- a/core.cpp +++ b/runtime/core.cpp @@ -1,7 +1,18 @@ #include "util/defs.h" +#include "sys.h" #include "stdlib.h" -#include "stdio.h" -#include "string.h" + +void* operator new[](unsigned long n) { + return malloc(n); +} + +void* operator new(unsigned long n, void* p) { + return p; +} + +void operator delete[](void* ptr) { + free(ptr); +} extern "C" void* _cons(i64 value, void* next) { void* result = malloc(sizeof(i64) + sizeof(void*)); @@ -20,69 +31,86 @@ extern "C" i64 _listlen(void* list) { } extern "C" void _display_int(i64 value) { - printf("%ld\n", value); + print(value, '\n'); +} + +extern "C" i64 _strlen(const char *s) { + const char *copy = s; + while (*s++); + return s - copy - 1; } static const char** symbol_table; extern "C" void _display_symbol(u64 value) { - printf("%s\n", symbol_table[value]); + write(_stdout, symbol_table[value], _strlen(symbol_table[value])); + write(_stdout, '\n'); } extern "C" void _display_bool(bool value) { - printf("%s\n", value ? "true" : "false"); + if (value) write(_stdout, "true", 5); + else write(_stdout, "false", 6); + write(_stdout, '\n'); } extern "C" void _display_string(const char* value) { - printf("%s\n", value); + write(_stdout, value, _strlen(value)); + write(_stdout, '\n'); } extern "C" void _display_int_list(void* value) { - printf("("); + write(_stdout, '('); bool first = true; while (value) { i64 i = *(i64*)value; - printf("%s%ld", first ? "" : " ", i); + if (!first) write(_stdout, ' '); + write(_stdout, i); value = *((void**)value + 1); first = false; } - printf(")\n"); + write(_stdout, ")\n", 3); } extern "C" void _display_bool_list(void* value) { - printf("("); + write(_stdout, '('); bool first = true; while (value) { u64 i = *(u64*)value; - printf("%s%s", first ? "" : " ", i ? "true" : "false"); + if (!first) write(_stdout, ' '); + if (i) write(_stdout, "true", 5); + else write(_stdout, "false", 6); value = *((void**)value + 1); first = false; } - printf(")\n"); + write(_stdout, ")\n", 3); } extern "C" void _display_symbol_list(void* value) { - printf("("); + write(_stdout, '('); bool first = true; while (value) { u64 i = *(u64*)value; - printf("%s%s", first ? "" : " ", symbol_table[i]); + if (!first) write(_stdout, ' '); + write(_stdout, symbol_table[i], _strlen(symbol_table[i])); value = *((void**)value + 1); first = false; } - printf(")\n"); + write(_stdout, ")\n", 3); } extern "C" void _display_native_string_list(void* value) { - printf("("); + write(_stdout, '('); bool first = true; while (value) { const char* i = *(const char**)value; - printf("%s%s%c", first ? "\"" : " \"", i, '"'); + if (!first) write(_stdout, ' '); + write(_stdout, '"'); + write(_stdout, i, _strlen(i)); + write(_stdout, '"'); value = *((void**)value + 1); first = false; } - printf(")\n"); + write(_stdout, ")\n", 3); } extern "C" i64 _strcmp(const char *a, const char *b) { @@ -90,16 +118,10 @@ extern "C" i64 _strcmp(const char *a, const char *b) { return *(const unsigned char*)a - *(const unsigned char*)b; } -extern "C" i64 _strlen(const char *s) { - const char *copy = s; - while (*s++); - return s - copy - 1; -} - extern "C" const u8* _read_line() { static char buffer[1024]; - scanf("%s", buffer); - int length = strlen(buffer); + read(_stdin, buffer, 1024); + int length = _strlen(buffer); u8* buf = new u8[length + 1]; for (u32 i = 0; i < length + 1; i ++) buf[i] = buffer[i]; buf[length] = '\0'; @@ -107,9 +129,7 @@ extern "C" const u8* _read_line() { } extern "C" i64 _read_int() { - i64 i; - scanf("%ld", &i); - return i; + return read(_stdin); } extern "C" const u8* _read_word() { diff --git a/runtime/sys.cpp b/runtime/sys.cpp new file mode 100644 index 0000000..bdc5d73 --- /dev/null +++ b/runtime/sys.cpp @@ -0,0 +1,318 @@ +#include "sys.h" + +extern void* _mmap(void*, u64, u64, u64) asm("_mmap"); +extern void _munmap(void*, u64) asm("_munmap"); +extern void _exit(u64) asm("_exit"); +extern i64 _read(u64, char*, u64) asm("_read"); +extern void _write(u64, const char*, u64) asm("_write"); + +/* * * * * * * * * * * * * * * * + * * + * System Calls * + * * + * * * * * * * * * * * * * * * */ + +inline void* _mmap(void* addr, u64 len, u64 prot, u64 flags) { + void* ret; + + #ifdef __APPLE__ + #define MMAP_CODE "0x20000C5" + #elif defined(__linux__) + #define MMAP_CODE "9" + #endif + + #define PROT_READ 0x1 + #define PROT_WRITE 0x2 + #define PROT_EXEC 0x4 + #define PROT_NONE 0x0 + #define PROT_GROWSDOWN 0x01000000 + #define PROT_GROWSUP 0x02000000 + + #define MAP_SHARED 0x01 + #define MAP_PRIVATE 0x02 + #define MAP_ANONYMOUS 0x20 + + asm volatile ("mov $" MMAP_CODE ", %%rax\n\t" + "mov %1, %%rdi\n\t" + "mov %2, %%rsi\n\t" + "mov %3, %%rdx\n\t" + "mov %4, %%r10\n\t" + "mov $-1, %%r8\n\t" + "mov $0, %%r9\n\t" + "syscall\n\t" + "mov %%rax, %0" + : "=r" (ret) + : "r" (addr), "r" (len), "r" (prot), "r" (flags) + : "rdi", "rsi", "rdx", "r10", "r9", "r8", "rax" + ); + + return ret; +} + +inline void _munmap(void* addr, u64 len) { + #ifdef __APPLE__ + #define MUNMAP_CODE "0x2000049" + #elif defined(__linux__) + #define MUNMAP_CODE "11" + #endif + + asm volatile ("mov $" MMAP_CODE ", %%rax\n\t" + "mov %0, %%rdi\n\t" + "mov %1, %%rsi\n\t" + "syscall\n\t" + : + : "r" (addr), "r" (len) + : "rdi", "rsi", "rax" + ); +} + +inline void _exit(u64 ret) { + #ifdef __APPLE__ + #define EXIT_CODE "0x2000001" + #elif defined(__linux__) + #define EXIT_CODE "60" + #endif + asm volatile ("mov $" EXIT_CODE ", %%rax\n\t" + "mov %0, %%rdi\n\t" + "syscall\n\t" + : + : "r" (ret) + : "rax", "rdi" + ); +} + +inline i64 _read(u64 fd, char* buf, u64 len) { + u64 ret; + #ifdef __APPLE__ + #define READ_CODE "0x2000003" + #elif defined(__linux__) + #define READ_CODE "0" + #endif + asm volatile ("mov $" READ_CODE ", %%rax\n\t" + "mov %1, %%rdi\n\t" + "mov %2, %%rsi\n\t" + "mov %3, %%rdx\n\t" + "syscall\n\t" + "mov %%rax, %0" + : "=r" (ret) + : "r" (fd), "r" (buf), "r" (len) + : "rax", "rdi", "rsi", "rdx" + ); + return ret; +} + +inline void _write(u64 fd, const char* text, u64 len) { + #ifdef __APPLE__ + #define WRITE_CODE "0x2000004" + #elif defined(__linux__) + #define WRITE_CODE "1" + #endif + asm volatile ("mov $" WRITE_CODE ", %%rax\n\t" + "mov %0, %%rdi\n\t" + "mov %1, %%rsi\n\t" + "mov %2, %%rdx\n\t" + "syscall\n\t" + : + : "r" (fd), "r" (text), "r" (len) + : "rax", "rdi", "rsi", "rdx" + ); +} + +void memcpy(void* dst, const void* src, int size) { + int i = 0; + while (i < (size & ~7)) *(u64*)dst = *(u64*)src, i += 8; + while (i < size) *(u8*)dst = *(u8*)src, i ++; +} + +void exit(u64 code) { + _exit(code); +} + +struct stream { + i32 fd; + u32 start, end; + char buf[4096]; +}; + +static stream internal_stdout = { 1, 0, 0 }, + internal_stdin = { 0, 0, 0 }; + +stream &_stdout = internal_stdout, + &_stdin = internal_stdin; + +static void flush_input(stream& io) { + memcpy(io.buf, io.buf + io.start, io.end - io.start); + io.end -= io.start, io.start = 0; + i64 amt = _read(io.fd, io.buf + io.end, 4096 - (io.end - io.start)); + io.end += amt; +} + +static void flush_output(stream& io) { + _write(io.fd, io.buf + io.start, io.end - io.start); + io.end = io.start; +} + +static void push_if_necessary(stream& io, u32 n = 64) { + if (4096 - io.end < n) flush_output(io); +} + +static void pull_if_necessary(stream& io, u32 n = 64) { + if (io.end - io.start < n) flush_input(io); +} + +static inline void put(stream& io, u8 c) { + io.buf[io.end ++] = c; + if (&io == &_stdout && c == '\n') flush_output(io); +} + +static inline void write_uint(stream& io, u64 u) { + if (!u) return put(io, '0'); + int c = 0, d = 0; + u64 p = 1; + while (p < u) p *= 10, ++ c; + d = c; + while (u) { + io.buf[io.end + c] = '0' + u % 10; + u /= 10; + -- c; + } + io.end += d + 1; +} + +static inline void write_int(stream& io, i64 i) { + if (i < 0) put(io, '-'), i = -i; + write_uint(io, i); +} + +void write(stream& io, const char* str, u32 n) { + push_if_necessary(io, (n + 63) / 64 * 64); + if (&io == &_stdout) for (u32 i = 0; i < n; i ++) { + io.buf[io.end ++] = str[i]; + if (str[i] == '\n') flush_output(io); + } + else memcpy(io.buf + io.end, str, n); +} + +void write(stream& io, const char& c) { + push_if_necessary(io); + put(io, c); +} + +void write(stream& io, const u8& c) { + push_if_necessary(io); + put(io, c); +} + +void write(stream& io, const u16& u) { + push_if_necessary(io); + write_uint(io, u); +} + +void write(stream& io, const u32& u) { + push_if_necessary(io); + write_uint(io, u); +} + +void write(stream& io, const u64& u) { + push_if_necessary(io); + write_uint(io, u); +} + +void write(stream& io, const i8& c) { + push_if_necessary(io); + put(io, c); +} + +void write(stream& io, const i16& i) { + push_if_necessary(io); + write_int(io, i); +} + +void write(stream& io, const i32& i) { + push_if_necessary(io); + write_int(io, i); +} + +void write(stream& io, const i64& i) { + push_if_necessary(io); + write_int(io, i); +} + +bool isdigit(char c) { + return c >= '0' && c <= '9'; +} + +u64 read_uint(stream& io) { + u64 acc = 0; + u8 i = 0; + while (isdigit(io.buf[io.start]) && i < 18) { + acc *= 10; + acc += io.buf[io.start] - '0'; + i ++; + io.start ++; + } + return acc; +} + +i64 read_int(stream& io) { + i64 i = 1; + if (io.buf[io.start] == '-') i = -1; + return i * read_uint(io); +} + +void read(stream& io, char* str, u32 n) { + while (n >= 4096) { + pull_if_necessary(io, (n + 63) / 64 * 64); + memcpy(str, io.buf + io.start, n); + n -= 4096; + } + pull_if_necessary(io, (n + 63) / 64 * 64); + memcpy(str, io.buf + io.start, n); +} + +void read(stream& io, u8& c) { + pull_if_necessary(io); + c = io.buf[io.start ++]; +} + +void read(stream& io, u16& u) { + pull_if_necessary(io); + u = read_uint(io); +} + +void read(stream& io, u32& u) { + pull_if_necessary(io); + u = read_uint(io); +} + +void read(stream& io, u64& u) { + pull_if_necessary(io); + u = read_uint(io); +} + +void read(stream& io, i8& c) { + pull_if_necessary(io); + c = io.buf[io.start ++]; +} + +void read(stream& io, i16& i) { + pull_if_necessary(io); + i = read_int(io); +} + +void read(stream& io, i32& i) { + pull_if_necessary(io); + i = read_int(io); +} + +void read(stream& io, i64& i) { + pull_if_necessary(io); + i = read_int(io); +} + +void flush(stream& io) { + flush_output(io); +} + +void write(stream& io) {} +void read(stream& io) {} \ No newline at end of file diff --git a/runtime/sys.h b/runtime/sys.h new file mode 100644 index 0000000..89247e5 --- /dev/null +++ b/runtime/sys.h @@ -0,0 +1,71 @@ +#ifndef FAST_IO_H +#define FAST_IO_H + +#include "util/defs.h" + +struct stream; + +extern stream &_stdout, &_stdin; + +void exit(u64 code); +extern "C" void memcpy(void* dst, const void* src, int size); + +void write(stream& io, const char* str, u32 n); +void write(stream& io, const char& c); +void write(stream& io, const u8& c); +void write(stream& io, const u16& u); +void write(stream& io, const u32& u); +void write(stream& io, const u64& u); +void write(stream& io, const i8& c); +void write(stream& io, const i16& u); +void write(stream& io, const i32& u); +void write(stream& io, const i64& u); +void read(stream& io, char* str, u32 n); +void read(stream& io, u8& c); +void read(stream& io, u16& c); +void read(stream& io, u32& c); +void read(stream& io, u64& c); +void read(stream& io, i8& c); +void read(stream& io, i16& c); +void read(stream& io, i32& c); +void read(stream& io, i64& c); + +void write(stream& io); +void read(stream& io); +void flush(stream& io); + +template +void write(stream& io, const T& t, const Args&... args) { + ::write(io, t); + write(io, args...); +} + +template +void read(stream& io, T& t, Args&... args) { + ::read(io, t); + read(io, args...); +} + +template +T read(stream& io) { + T t; + ::read(io, t); + return t; +} + +template +void print(const Args&... args) { + write(_stdout, args...); +} + +template +void input(Args&... args) { + read(_stdin, args...); +} + +template +T input() { + return read(_stdin); +} + +#endif \ No newline at end of file diff --git a/util/str.cpp b/util/str.cpp index 6447c17..aee1f2d 100644 --- a/util/str.cpp +++ b/util/str.cpp @@ -126,6 +126,10 @@ slice string::operator[](pair range) { return { range.second - range.first, data + range.first }; } +bool string::endswith(u8 c) const { + return data[_size - 1] == c; +} + const u8* string::raw() const { return data; } diff --git a/util/str.h b/util/str.h index 5fdffdd..3780852 100644 --- a/util/str.h +++ b/util/str.h @@ -35,6 +35,7 @@ class string { u8& operator[](u32 i); const_slice operator[](pair range) const; slice operator[](pair range); + bool endswith(u8 c) const; const u8* raw() const; bool operator==(const u8* s) const; bool operator==(const char* s) const; From f947a8d1c345da0c5229a63cb04ba435955b9dad Mon Sep 17 00:00:00 2001 From: elucent <9532786+elucent@users.noreply.github.com> Date: Sun, 7 Feb 2021 13:13:26 -0500 Subject: [PATCH 09/17] Misc. changes. --- .gitignore | 2 +- Makefile | 2 +- compiler/ast.cpp | 24 +++++++-- compiler/ast.h | 5 +- compiler/builtin.cpp | 49 +++++++++++++++++-- compiler/builtin.h | 4 +- compiler/driver.cpp | 37 +++++++------- compiler/eval.cpp | 102 ++++++++++++++++++++++---------------- compiler/ir.cpp | 9 +++- compiler/ir.h | 2 +- compiler/ops.cpp | 10 ++++ compiler/ops.h | 1 + compiler/parse.cpp | 1 + compiler/type.cpp | 1 + compiler/values.cpp | 113 ++++++++++++++++++++++++++++++++++++------- compiler/values.h | 15 ++++++ example/extern.bl | 4 ++ example/fibonacci | Bin 4920 -> 0 bytes example/fibonacci.bl | 3 +- example/fibonacci.c | 11 ----- jasmine/obj.cpp | 6 +-- jasmine/x64.cpp | 21 +++++--- run-tests.sh | 13 +++++ test/math.bl | 27 +++++++++++ test/rational.bl | 8 +++ 25 files changed, 353 insertions(+), 117 deletions(-) create mode 100644 example/extern.bl delete mode 100644 example/fibonacci delete mode 100644 example/fibonacci.c create mode 100644 run-tests.sh create mode 100644 test/math.bl create mode 100644 test/rational.bl diff --git a/.gitignore b/.gitignore index 2bd7ea1..4f4208a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .vscode/ *.o -TODO basil librt.a +TODO \ No newline at end of file diff --git a/Makefile b/Makefile index 68e91f7..7ed9ff1 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ OBJS := $(patsubst %.cpp,%.o,$(SRCS)) CXX := clang++ CXXHEADERS := -I. -Iutil -Ijasmine -Icompiler -CXXFLAGS := $(CXXHEADERS) -std=c++11 -Os -ffast-math -fno-rtti -fno-exceptions -Wno-null-dereference +CXXFLAGS := $(CXXHEADERS) -std=c++17 -ffast-math -fno-rtti -fno-exceptions -Wno-null-dereference LDFLAGS := -Wl,--unresolved-symbols=ignore-in-object-files clean: diff --git a/compiler/ast.cpp b/compiler/ast.cpp index 107db88..b9f98ef 100644 --- a/compiler/ast.cpp +++ b/compiler/ast.cpp @@ -18,6 +18,10 @@ namespace basil { return _type->concretify(); } + bool ASTNode::is_extern() const { + return false; + } + ASTSingleton::ASTSingleton(const Type* type): ASTNode(NO_LOCATION), _type(type) {} @@ -159,11 +163,11 @@ namespace basil { write(io, symbol_for(_name)); } - ASTExtern::ASTExtern(SourceLocation loc): - ASTNode(loc) {} + ASTExtern::ASTExtern(SourceLocation loc, const Type* type): + ASTNode(loc), _type(type) {} const Type* ASTExtern::lazy_type() { - return find(); + return _type; } ref ASTExtern::emit(ref& parent) { @@ -178,6 +182,10 @@ namespace basil { write(io, "extern"); } + bool ASTExtern::is_extern() const { + return true; + } + ASTUnary::ASTUnary(SourceLocation loc, ASTNode* child): ASTNode(loc), _child(child) { _child->inc(); @@ -582,7 +590,15 @@ namespace basil { } Location ASTFunction::emit(Function& func) { - if (!_emitted) { + if (_body->is_extern()) { + _emitted = true; + _label = add_label(symbol_for(_name)); + Location loc; + loc.type = LOC_LABEL; + loc.label_index = _label; + return func.add(new AddressInsn(loc, type())); + } + else if (!_emitted) { _emitted = true; Function& fn = _name == -1 ? func.create_function() : func.create_function(symbol_for(_name)); diff --git a/compiler/ast.h b/compiler/ast.h index 9c5688d..fa910aa 100644 --- a/compiler/ast.h +++ b/compiler/ast.h @@ -26,6 +26,7 @@ namespace basil { SourceLocation loc() const; const Type* type(); + virtual bool is_extern() const; virtual ref emit(ref& parent) = 0; virtual Location emit(Function& function) = 0; virtual void format(stream& io) const = 0; @@ -116,11 +117,13 @@ namespace basil { }; class ASTExtern : public ASTNode { + const Type* _type; protected: const Type* lazy_type() override; public: - ASTExtern(SourceLocation loc); + ASTExtern(SourceLocation loc, const Type* type); + bool is_extern() const override; ref emit(ref& parent) override; Location emit(Function& function) override; void format(stream& io) const override; diff --git a/compiler/builtin.cpp b/compiler/builtin.cpp index f73554a..11cf182 100644 --- a/compiler/builtin.cpp +++ b/compiler/builtin.cpp @@ -33,16 +33,24 @@ namespace basil { #define ARG(n) args.get_product()[n] - Builtin ADD, SUB, MUL, DIV, REM, AND, OR, XOR, NOT, EQUALS, NOT_EQUALS, LESS, GREATER, LESS_EQUAL, GREATER_EQUAL, + Builtin ADD_INT, ADD_SYMBOL, SUB, MUL, DIV, REM, + AND, OR, XOR, NOT, + EQUALS, NOT_EQUALS, LESS, GREATER, LESS_EQUAL, GREATER_EQUAL, IS_EMPTY, HEAD, TAIL, CONS, DISPLAY, READ_LINE, READ_WORD, READ_INT, LENGTH, - AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, STRCAT, SUBSTR, ANNOTATE, TYPEOF, LIST_TYPE, ASSIGN, IF; + AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, STRCAT, SUBSTR, ANNOTATE, TYPEOF, LIST_TYPE, OF_TYPE_MACRO, + OF_TYPE, ASSIGN, IF; static void init_builtins() { - ADD = {find(find(INT, INT), INT), + ADD_INT = {find(find(INT, INT), INT), [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() + ARG(1).get_int()); }, [](ref env, const Value& args) -> Value { return Value(new ASTBinaryMath(ARG(0).loc(), AST_ADD, ARG(0).get_runtime(), ARG(1).get_runtime())); }}; + ADD_SYMBOL = {find(find(SYMBOL, SYMBOL), SYMBOL), + [](ref env, const Value& args) -> Value { + return Value(symbol_for(ARG(0).get_symbol()) + symbol_for(ARG(1).get_symbol()), SYMBOL); + }, + nullptr}; SUB = {find(find(INT, INT), INT), [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() - ARG(1).get_int()); }, [](ref env, const Value& args) -> Value { @@ -197,13 +205,41 @@ namespace basil { }, nullptr }; + ANNOTATE = { + find(find(ANY, TYPE), ANY), + [](ref env, const Value& args) -> Value { + if (!ARG(0).type()->coerces_to(ARG(1).get_type())) { + err(ARG(0).loc(), "Could not unify value of type '", ARG(0).type(), "' with type '", + ARG(1).get_type(), "'."); + return error(); + } + return cast(ARG(0), ARG(1).get_type()); + }, + [](ref env, const Value& args) -> Value { + return new ASTAnnotate(ARG(0).loc(), lower(ARG(0)).get_runtime(), ARG(1).get_type()); + }, + NO_AUTO_LOWER + }; TYPEOF = {find(find(ANY), TYPE), [](ref env, const Value& args) -> Value { return Value(ARG(0).type(), TYPE); }, nullptr}; LIST_TYPE = { find(find(TYPE), TYPE), [](ref env, const Value& args) -> Value { return Value(find(ARG(0).get_type()), TYPE); }, nullptr}; - + OF_TYPE_MACRO = { + find(2), + [](ref env, const Value& args) -> Value { + return list_of(Value("#of", SYMBOL), list_of(Value("quote", SYMBOL), ARG(0)), ARG(1)); + }, + nullptr + }; + OF_TYPE = { + find(find(SYMBOL, TYPE), TYPE), + [](ref env, const Value& args) -> Value { + return Value(find(symbol_for(ARG(0).get_symbol()), ARG(1).get_type()), TYPE); + }, + nullptr + }; IF = { find(find(BOOL, ANY, ANY), ANY), [](ref env, const Value& args) -> Value { return eval(env, ARG(ARG(0).get_bool() ? 1 : 2)); }, @@ -231,7 +267,7 @@ namespace basil { void define_builtins(ref env) { if (!inited) inited = true, init_builtins(); - env->infix("+", Value(env, ADD), 2, 20); + env->infix("+", cases(env, &ADD_INT, &ADD_SYMBOL), 2, 20); env->infix("-", Value(env, SUB), 2, 20); env->infix("*", Value(env, MUL), 2, 40); env->infix("/", Value(env, DIV), 2, 40); @@ -248,7 +284,10 @@ namespace basil { env->infix(">=", Value(env, GREATER_EQUAL), 2, 10); env->def("display", Value(env, DISPLAY), 1); env->infix("at", cases(env, &AT_INT, &AT_LIST, &AT_ARRAY_TYPE, &AT_DYNARRAY_TYPE), 2, 120); + env->def("annotate", Value(env, ANNOTATE), 2); env->def("typeof", Value(env, TYPEOF), 1); + env->infix_macro("of", Value(env, OF_TYPE_MACRO), 2, 20); + env->def("#of", Value(env, OF_TYPE), 2); env->infix("list", Value(env, LIST_TYPE), 1, 80); env->infix("#?", Value(env, IF), 3, 2); } diff --git a/compiler/builtin.h b/compiler/builtin.h index cce5005..527875e 100644 --- a/compiler/builtin.h +++ b/compiler/builtin.h @@ -33,13 +33,13 @@ namespace basil { extern void define_builtins(ref env); extern Builtin - ADD, SUB, MUL, DIV, REM, + ADD_INT, ADD_SYMBOL, SUB, MUL, DIV, REM, AND, OR, XOR, NOT, EQUALS, NOT_EQUALS, LESS, GREATER, LESS_EQUAL, GREATER_EQUAL, IS_EMPTY, HEAD, TAIL, CONS, DISPLAY, READ_LINE, READ_WORD, READ_INT, LENGTH, AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, STRCAT, SUBSTR, - ANNOTATE, TYPEOF, LIST_TYPE, + ANNOTATE, TYPEOF, LIST_TYPE, OF_TYPE_MACRO, OF_TYPE, ASSIGN, IF; } diff --git a/compiler/driver.cpp b/compiler/driver.cpp index 10975a3..fffded1 100644 --- a/compiler/driver.cpp +++ b/compiler/driver.cpp @@ -153,15 +153,15 @@ namespace basil { print("? "); auto view = src.expand(_stdin); auto tokens = lex(view); - if (error_count()) return print_errors(_stdout), error(); + if (error_count()) return print_errors(_stdout, src), error(); TokenView tview(tokens, src, true); Value program = parse(tview); - if (error_count()) return print_errors(_stdout), error(); + if (error_count()) return print_errors(_stdout, src), error(); prep(global, program); Value result = eval(global, program); - if (error_count()) return print_errors(_stdout), error(); + if (error_count()) return print_errors(_stdout, src), error(); if (!result.is_runtime()) { if (!result.is_void()) @@ -176,7 +176,7 @@ namespace basil { compile(result, object, mainfn); add_native_functions(object); object.load(); - if (error_count()) return print_errors(_stdout), error(); + if (error_count()) return print_errors(_stdout, src), error(); print(BOLDBLUE); jit_print(result, object); @@ -187,17 +187,17 @@ namespace basil { ref load(Source& src) { auto view = src.begin(); auto tokens = lex(view); - if (error_count()) return print_errors(_stdout), nullptr; + if (error_count()) return print_errors(_stdout, src), nullptr; TokenView tview(tokens, src); Value program = parse(tview); - if (error_count()) return print_errors(_stdout), nullptr; + if (error_count()) return print_errors(_stdout, src), nullptr; ref global = create_global_env(); prep(global, program); Value result = eval(global, program); - if (error_count()) return print_errors(_stdout), nullptr; + if (error_count()) return print_errors(_stdout, src), nullptr; return global; } @@ -205,17 +205,17 @@ namespace basil { int run(Source& src) { auto view = src.begin(); auto tokens = lex(view); - if (error_count()) return print_errors(_stdout), 1; + if (error_count()) return print_errors(_stdout, src), 1; TokenView tview(tokens, src); Value program = parse(tview); - if (error_count()) return print_errors(_stdout), 1; + if (error_count()) return print_errors(_stdout, src), 1; ref global = create_global_env(); prep(global, program); Value result = eval(global, program); - if (error_count()) return print_errors(_stdout), 1; + if (error_count()) return print_errors(_stdout, src), 1; if (!result.is_runtime()) return 0; if (_print_ast) @@ -223,12 +223,9 @@ namespace basil { jasmine::Object object; compile(result, object); - auto code = object.code(); - while (code.size()) printf("%02x ", code.read()); - printf("\n"); add_native_functions(object); object.load(); - if (error_count()) return print_errors(_stdout), 1; + if (error_count()) return print_errors(_stdout, src), 1; return execute(result, object); } @@ -258,17 +255,17 @@ namespace basil { string dest = change_ending(filename, ".o"); auto view = src.begin(); auto tokens = lex(view); - if (error_count()) return print_errors(_stdout), 1; + if (error_count()) return print_errors(_stdout, src), 1; TokenView tview(tokens, src); Value program = parse(tview); - if (error_count()) return print_errors(_stdout), 1; + if (error_count()) return print_errors(_stdout, src), 1; ref global = create_global_env(); prep(global, program); Value result = eval(global, program); - if (error_count()) return print_errors(_stdout), 1; + if (error_count()) return print_errors(_stdout, src), 1; auto insts = instantiations(global); if (result.is_runtime()) { @@ -277,7 +274,7 @@ namespace basil { jasmine::Object object; compile(result, object); - if (error_count()) return print_errors(_stdout), 1; + if (error_count()) return print_errors(_stdout, src), 1; object.writeELF((const char*)dest.raw()); } else if (insts.size() > 0) { @@ -290,9 +287,9 @@ namespace basil { println(RESET, "\n"); } container.allocate(); - container.emit(object); + container.emit(object, true); emit_constants(object); - if (error_count()) return print_errors(_stdout), 1; + if (error_count()) return print_errors(_stdout, src), 1; object.writeELF((const char*)dest.raw()); } diff --git a/compiler/eval.cpp b/compiler/eval.cpp index 571e4b0..c9a6941 100644 --- a/compiler/eval.cpp +++ b/compiler/eval.cpp @@ -202,12 +202,12 @@ namespace basil { root->def("true", Value(true, BOOL)); root->def("false", Value(false, BOOL)); - root->def("extern", Value(new ASTExtern({}))); root->def("int", Value(INT, TYPE)); root->def("symbol", Value(SYMBOL, TYPE)); root->def("string", Value(STRING, TYPE)); root->def("type", Value(TYPE, TYPE)); root->def("bool", Value(BOOL, TYPE)); + root->def("void", Value(VOID, TYPE)); return root; } @@ -215,7 +215,7 @@ namespace basil { Value eval_list(ref env, const Value& list); Value eval(ref env, Value term); - Value define(ref env, const Value& term, bool is_macro); + Value define(ref env, const Value& term, bool is_macro, bool only_vars = false, bool only_procs = false); // utilities @@ -404,6 +404,22 @@ namespace basil { return symbol_for(head(tail(term)).get_symbol()); } + u64 get_keyword(const Value& v) { + if (v.is_symbol()) return v.get_symbol(); + else if (v.is_list() && head(v).is_symbol() && head(v).get_symbol() == symbol_value("quote") && + tail(v).is_list() && head(tail(v)).is_symbol()) + return head(tail(v)).get_symbol(); + return 0; + } + + bool is_keyword(const Value& v, const string& word) { + if (v.is_symbol()) return v.get_symbol() == symbol_value(word); + else if (v.is_list() && head(v).is_symbol() && head(v).get_symbol() == symbol_value("quote") && + tail(v).is_list() && head(tail(v)).is_symbol()) + return head(tail(v)).get_symbol() == symbol_value(word); + return false; + } + void visit_defs(ref env, const Value& item) { if (!item.is_list()) return; Value h = head(item); @@ -473,6 +489,15 @@ namespace basil { } else traverse_list(env, item, visit_defs); } + + void define_procedures(ref env, const Value& item) { + if (!item.is_list()) return; + Value h = head(item); + if (symbol_matches(h, "def")) { + define(env, item, false, false, true); + } + else traverse_list(env, item, define_procedures); + } void handle_macro(ref env, Value& item); @@ -489,7 +514,7 @@ namespace basil { const MacroValue& fn = macro.get_macro(); if (fn.is_builtin()) { - return error(); // fn.get_builtin()(env, arg); + return fn.get_builtin().eval(env, arg); } else { ref env = fn.get_env(); u32 argc = arg.get_product().size(), arity = fn.args().size(); @@ -500,10 +525,9 @@ namespace basil { for (u32 i = 0; i < arity; i++) { if (fn.args()[i] & KEYWORD_ARG_BIT) { // keyword arg - Value argument = eval(env, arg.get_product()[i]); - if (!argument.is_symbol() || argument.get_symbol() != (fn.args()[i] & ARG_NAME_MASK)) { + if (get_keyword(arg.get_product()[i]) != (fn.args()[i] & ARG_NAME_MASK)) { err(arg.get_product()[i].loc(), "Expected keyword '", symbol_for(fn.args()[i] & ARG_NAME_MASK), - "', got '", argument, "'."); + "', got '", arg.get_product()[i], "'."); return error(); } } else { @@ -666,17 +690,17 @@ namespace basil { } void apply_infix(ref env, Value& item) { - Value orig = item.clone(); if (!item.is_list()) return; Value h = head(item); if (h.is_symbol()) { const string& name = symbol_for(h.get_symbol()); - if (name == "macro" || name == "infix" || name == "infix-macro" || name == "lambda" || name == "quote" || + if (name == "macro" || name == "lambda" || name == "quote" || name == "splice") return; - else if (name == "def") { - if (tail(item).is_list() && head(tail(item)).is_symbol()) apply_infix_at(env, item, 2); - } else if (name == "if" || name == "do" || name == "list-of") + // else if (name == "def") { + // if (tail(item).is_list() && head(tail(item)).is_symbol()) apply_infix_at(env, item, 2); + // } + else if (name == "if" || name == "do" || name == "list-of") apply_infix_at(env, item, 1); else { silence_errors(); @@ -694,11 +718,12 @@ namespace basil { handle_splice(env, term); handle_use(env, term); visit_macro_defs(env, term); - handle_macro(env, term); visit_defs(env, term); apply_infix(env, term); handle_macro(env, term); visit_defs(env, term); + apply_infix(env, term); + define_procedures(env, term); } Value overload(ref env, Def* existing, const Value& func, const string& name) { @@ -742,7 +767,7 @@ namespace basil { } // definition stuff - Value define(ref env, const Value& term, bool is_macro) { + Value define(ref env, const Value& term, bool is_macro, bool only_vars, bool only_procs) { vector values = to_vector(term); // visit_defs already does some error-checking, so we @@ -757,6 +782,7 @@ namespace basil { } if (is_valid_variable(values[i])) { // variable + if (only_procs) return Value(VOID); string name = get_variable_name(values[i]); if (is_macro) { @@ -776,6 +802,7 @@ namespace basil { return Value(VOID); } } else if (values[i].is_list()) { // procedure + if (only_vars) return Value(VOID); bool infix = !is_valid_def(values[i]); string name = infix ? get_infix_name(values[i]) : get_def_name(values[i]); @@ -842,16 +869,31 @@ namespace basil { } returntype = tval.get_type(); } + + bool external = false; + if (body.size() == 1 && symbol_matches(body[0], "extern")) { + if (!returntype->concrete()) { + err(body[0].loc(), "Explicit return type required in extern function definition."); + return error(); + } + external = true; + } + const Type* argst = find(argts); - Value func(new FunctionValue(function_env, argnames, body_term, symbol_value(name)), - find(argst, returntype)); + const FunctionType* ft = (const FunctionType*)find(argst, returntype); + u64 fname = symbol_value(name); + Value func = external ? Value(new ASTFunction(term.loc(), function_env, argst, argnames, + new ASTExtern(term.loc(), returntype), fname)) + : Value(new FunctionValue(function_env, argnames, body_term, fname), ft); Def* existing = env->find(name); if (existing && !existing->value.is_void()) { return overload(env, existing, func, name); } else if (infix) { env->infix(name, func, argnames.size(), precedence); } else env->def(name, func, argnames.size()); - if (argst->concrete()) { + if (func.is_runtime()) + return new ASTDefine(values[0].loc(), env, fname, func.get_runtime()); + if (argst->concrete() && !external) { func.get_function().instantiate(argst, new ASTIncompleteFn(func.loc(), argst, symbol_value(name))); instantiate(func.loc(), func.get_function(), find(argts)); @@ -1058,22 +1100,6 @@ namespace basil { return values.back(); } - u64 get_keyword(const Value& v) { - if (v.is_symbol()) return v.get_symbol(); - else if (v.is_list() && head(v).is_symbol() && head(v).get_symbol() == symbol_value("quote") && - tail(v).is_list() && head(tail(v)).is_symbol()) - return head(tail(v)).get_symbol(); - return 0; - } - - bool is_keyword(const Value& v, const string& word) { - if (v.is_symbol()) return v.get_symbol() == symbol_value(word); - else if (v.is_list() && head(v).is_symbol() && head(v).get_symbol() == symbol_value("quote") && - tail(v).is_list() && head(tail(v)).is_symbol()) - return head(tail(v)).get_symbol() == symbol_value(word); - return false; - } - Value if_expr(ref env, const Value& term) { Value params = tail(term); prep(env, params); @@ -1251,13 +1277,9 @@ namespace basil { const string& name = symbol_for(h.get_symbol()); if (name == "quote") return head(tail(term)).clone(); else if (name == "def") - return define(env, term, false); - // else if (name == "infix") - // return infix(env, term, false); + return define(env, term, false, true); else if (name == "macro") return define(env, term, true); - // else if (name == "infix-macro") - // return infix(env, term, true); else if (name == "lambda") return lambda(env, term); else if (name == "do") @@ -1288,11 +1310,7 @@ namespace basil { const Value* v = &term.get_list().tail(); u32 i = 0; while (v->is_list()) { - if (i < first.get_macro().arity() && first.get_macro().args()[i] & KEYWORD_ARG_BIT && - v->get_list().head().is_symbol()) - args.push(list_of(Value("quote"), v->get_list().head())); - else - args.push(v->get_list().head()); + args.push(v->get_list().head()); v = &v->get_list().tail(); i++; } diff --git a/compiler/ir.cpp b/compiler/ir.cpp index ebdb0c7..ab1c152 100644 --- a/compiler/ir.cpp +++ b/compiler/ir.cpp @@ -259,6 +259,11 @@ namespace basil { reg_stack.push(all_locals[k].reg); } } + + for (u32 i = 0; i < all_locals.size(); i ++) { + if (all_locals[i].reg == -1 && all_locals[i].offset == 0) + all_locals[i].reg = RAX; // clobber RAX for dead code (for now) + } } Function::Function(u32 label): @@ -323,7 +328,7 @@ namespace basil { to_registers(); } - void Function::emit(Object& obj) { + void Function::emit(Object& obj, bool exit) { for (Function* fn : _fns) fn->emit(obj); writeto(obj); @@ -331,7 +336,7 @@ namespace basil { open_frame(_stack); for (Insn* i : _insns) i->emit(); local_label(all_labels[_end]); - if (_fns.size() > 0) { // hack + if (exit && all_labels[_label] == "_start") { mov(x64::r64(x64::RAX), x64::imm64(60)); mov(x64::r64(x64::RDI), x64::imm64(0)); syscall(); diff --git a/compiler/ir.h b/compiler/ir.h index 4882ce7..4ca8c5c 100644 --- a/compiler/ir.h +++ b/compiler/ir.h @@ -151,7 +151,7 @@ namespace basil { Location add(Insn* insn); u32 label() const; void allocate(); - void emit(Object& obj); + void emit(Object& obj, bool exit = false); void format(stream& io) const; u32 end_label() const; const Location& ret_loc() const; diff --git a/compiler/ops.cpp b/compiler/ops.cpp index 67fbb53..135db15 100644 --- a/compiler/ops.cpp +++ b/compiler/ops.cpp @@ -460,6 +460,16 @@ namespace basil { } } + void call(const Location& func) { + switch (_arch) { + case X86_64: + x64::call(x64_arg(func)); + return; + default: + return; + } + } + void call(const Location& dest, const Location& func) { switch (_arch) { case X86_64: diff --git a/compiler/ops.h b/compiler/ops.h index ed47900..2e2db3d 100644 --- a/compiler/ops.h +++ b/compiler/ops.h @@ -38,6 +38,7 @@ namespace basil { void jump_if_zero(const Location& dest, const Location& cond); void set_arg(u32 i, const Location& src); void get_arg(const Location& dest, u32 i); + void call(const Location& func); void call(const Location& dest, const Location& func); void global_label(const string& name); void local_label(const string& name); diff --git a/compiler/parse.cpp b/compiler/parse.cpp index fe9af6f..8a1da6f 100644 --- a/compiler/parse.cpp +++ b/compiler/parse.cpp @@ -59,6 +59,7 @@ namespace basil { while (view.peek().type != terminator) { while (view.peek().type == T_NEWLINE) view.read(); if (!view.peek() && out_of_input(view)) return; + if (view.peek().type == terminator) break; terms.push(parse(view, indent)); if (view.peek().type == T_COMMA) { if (terminator != T_RPAREN && !errored) { // not enclosed by parens diff --git a/compiler/type.cpp b/compiler/type.cpp index e432ff8..74f546f 100644 --- a/compiler/type.cpp +++ b/compiler/type.cpp @@ -21,6 +21,7 @@ namespace basil { || other == ANY || other->kind() == KIND_RUNTIME && this->coerces_to(((const RuntimeType*)other)->base()) || this == VOID && other->kind() == KIND_LIST + || other->kind() == KIND_NAMED && ((const NamedType*)other)->base() == this || other->kind() == KIND_SUM && ((const SumType*)other)->has(this); } diff --git a/compiler/values.cpp b/compiler/values.cpp index 48ad46c..bc957a8 100644 --- a/compiler/values.cpp +++ b/compiler/values.cpp @@ -93,6 +93,10 @@ namespace basil { _data.rc = m; } + Value::Value(NamedValue* n, const Type* t) : _type(t) { + _data.rc = n; + } + Value::Value(ASTNode* n) : _type(n->type()->kind() == KIND_RUNTIME ? n->type() : find(n->type())) { _data.rc = n; @@ -290,6 +294,18 @@ namespace basil { ASTNode*& Value::get_runtime() { return (ASTNode*&)_data.rc; + } + + bool Value::is_named() const { + return _type->kind() == KIND_NAMED; + } + + const NamedValue& Value::get_named() const { + return *(const NamedValue*)_data.rc; + } + + NamedValue& Value::get_named() { + return *(NamedValue*)_data.rc; } const Type* Value::type() const { @@ -310,6 +326,8 @@ namespace basil { write(io, get_type()); else if (is_bool()) write(io, get_bool()); + else if (is_named()) + write(io, ((const NamedType*)type())->name(), "(", get_named().get(), ")"); else if (is_list()) { bool first = true; write(io, "("); @@ -370,6 +388,8 @@ namespace basil { return get_type()->hash(); else if (is_bool()) return get_bool() ? 9269586835432337327ul : 18442604092978916717ul; + else if (is_named()) + return get_named().get().hash() ^ (5789283014586986071ul * ::hash(((const NamedType*)type())->name())); else if (is_list()) { u64 h = 9572917161082946201ul; Value ptr = *this; @@ -428,6 +448,8 @@ namespace basil { return get_bool() == other.get_bool(); else if (is_string()) return get_string() == other.get_string(); + else if (is_named()) + return get_named().get() == other.get_named().get(); else if (is_sum()) return get_sum().value() == other.get_sum().value(); else if (is_intersect()) { @@ -480,11 +502,20 @@ namespace basil { } Value Value::clone() const { - if (is_list()) return Value(new ListValue(get_list().head().clone(), get_list().tail().clone())); + if (is_list()) { + ListValue* l = nullptr; + const ListValue* i = &get_list(); + vector vals; + while (i) vals.push(&i->head()), i = i->tail().is_void() ? nullptr : &i->tail().get_list(); + for (i64 i = i64(vals.size()) - 1; i >= 0; i --) l = new ListValue(vals[i]->clone(), l ? l : empty()); + return Value(l); + } else if (is_string()) return Value(get_string(), STRING); + else if (is_named()) + return Value(new NamedValue(get_named().get().clone()), type()); else if (is_sum()) - return Value(new SumValue(get_sum().value()), type()); + return Value(new SumValue(get_sum().value().clone()), type()); else if (is_intersect()) { map values; for (const auto& p : get_intersect()) values.put(p.first, p.second.clone()); @@ -544,6 +575,17 @@ namespace basil { return _value; } + NamedValue::NamedValue(const Value& inner): + _inner(inner) {} + + Value& NamedValue::get() { + return _inner; + } + + const Value& NamedValue::get() const { + return _inner; + } + ListValue::ListValue(const Value& head, const Value& tail) : _head(head), _tail(tail) {} Value& ListValue::head() { @@ -1203,6 +1245,13 @@ namespace basil { return ((const RuntimeType*)type)->base() == ANY ? val : lower(val); } + if (type->kind() == KIND_NAMED) { + return Value(new NamedValue(val), type); + } + else if (val.type()->kind() == KIND_NAMED && type == ((const NamedType*)val.type())->base()) { + return val.get_named().get(); + } + if (val.is_product()) { if (type == TYPE) { vector ts; @@ -1382,7 +1431,13 @@ namespace basil { function = function.get_intersect().values()[ftypes.begin()->first]; } - if (!function.is_function()) { + if (function.is_runtime()) { + if (((const RuntimeType*)function.type())->base()->kind() != KIND_FUNCTION) { + err(function.loc(), "Cannot call non-function value '", function, "'."); + return error(); + } + } + else if (!function.is_function()) { err(function.loc(), "Cannot call non-function value '", function, "'."); return error(); } @@ -1391,7 +1446,9 @@ namespace basil { return error(); } - const FunctionType* ft = (const FunctionType*)function.type(); + const FunctionType* ft = function.is_runtime() ? + ((const FunctionType*)((const RuntimeType*)function.type())->base()) + : (const FunctionType*)function.type(); const ProductType* argst = (const ProductType*)ft->arg(); if (args.get_product().size() != argst->count()) { @@ -1407,16 +1464,21 @@ namespace basil { break; } } - FunctionValue& fn = function.get_function(); - if (fn.is_builtin()) { - if (fn.get_builtin().runtime_only()) has_runtime = true; - } - else { - if (!fn.found_calls()) { - set visited; - find_calls(fn, env, fn.body(), visited); + + if (function.is_runtime()) has_runtime = true; + if (function.is_function()) { + FunctionValue& fn = function.get_function(); + if (fn.is_builtin()) { + if (fn.get_builtin().runtime_only()) has_runtime = true; + } + else { + if (ft->ret()->kind() == KIND_RUNTIME) has_runtime = true; + if (!fn.found_calls()) { + set visited; + find_calls(fn, env, fn.body(), visited); + } + // if (fn.recursive()) has_runtime = true; } - if (fn.recursive()) has_runtime = true; } const ProductType* rtargst = argst; @@ -1441,6 +1503,18 @@ namespace basil { if (argt != rtargst->member(i)) args_copy.get_product()[i] = cast(args.get_product()[i], rtargst->member(i)); } + if (function.is_runtime()) { + vector rtargs; + for (u32 i = 0; i < ft->arity(); i++) { + Value v = lower(args_copy.get_product()[i]); + ASTNode* n = v.get_runtime(); + n->inc(); + rtargs.push(n); + } + return new ASTCall(function.loc(), function.get_runtime(), rtargs); + } + + FunctionValue& fn = function.get_function(); if (fn.is_builtin()) { if (has_runtime && fn.get_builtin().should_lower()) for (Value& v : args_copy.get_product()) v = lower(v); @@ -1449,6 +1523,7 @@ namespace basil { } else { vector rtargs; ref fnenv = fn.get_env(); + map bindings; for (u32 i = 0; i < fn.arity(); i++) { if (fn.args()[i] & KEYWORD_ARG_BIT) { // keyword arg @@ -1466,7 +1541,11 @@ namespace basil { n->inc(); rtargs.push(n); } - else fnenv->find(argname)->value = args_copy.get_product()[i]; + else { + Def* def = fnenv->find(argname); + bindings[argname] = def->value; + def->value = args_copy.get_product()[i]; + } } } if (has_runtime) { @@ -1479,9 +1558,9 @@ namespace basil { return new ASTCall(callable.loc(), body, rtargs); } else { - Value prepped = fn.body().clone(); - prep(fnenv, prepped); - return eval(fnenv, prepped); + Value result = eval(fnenv, fn.body()); + for (auto& p : bindings) fnenv->find(p.first)->value = p.second; + return result; } } } diff --git a/compiler/values.h b/compiler/values.h index 2dea420..ac3bd8d 100644 --- a/compiler/values.h +++ b/compiler/values.h @@ -26,6 +26,7 @@ namespace basil { class FunctionValue; class AliasValue; class MacroValue; + class NamedValue; class Value { const Type* _type; @@ -55,6 +56,7 @@ namespace basil { Value(FunctionValue* f, const Type* ftype); Value(AliasValue* f); Value(MacroValue* f); + Value(NamedValue* n, const Type* t); Value(ASTNode* n); ~Value(); Value(const Value& other); @@ -124,6 +126,10 @@ namespace basil { ASTNode* get_runtime() const; ASTNode*& get_runtime(); + bool is_named() const; + const NamedValue& get_named() const; + NamedValue& get_named(); + const Type* type() const; void format(stream& io) const; u64 hash() const; @@ -144,6 +150,15 @@ namespace basil { const string& value() const; }; + class NamedValue : public RC { + Value _inner; + public: + NamedValue(const Value& inner); + + Value& get(); + const Value& get() const; + }; + class ListValue : public RC { Value _head, _tail; public: diff --git a/example/extern.bl b/example/extern.bl new file mode 100644 index 0000000..93b3b8c --- /dev/null +++ b/example/extern.bl @@ -0,0 +1,4 @@ +def (puts s?: string): void extern + +puts "hello world!" + diff --git a/example/fibonacci b/example/fibonacci deleted file mode 100644 index 327445fd399e7fd09d974f3822b79d9a20a118a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4920 zcmeI0ze~eV5XY}+6$PbL6#RiuMGyyzKM-`0Iw%RIgTIzKl++*SU<*x#IyuxOL-!8; z5&j1TJJe0w+;psyxD^V=kP1izqyka_sen{KDj*g3y8^1+R7*`ot2xxO9=&$7QUjt_X^b@c>GkF9*44{f zuDYw+5A_+3-l+$OKC~S|Lx@zns-;uqzOj^juiH*lt<~lb*Bh?uVotT6>lJi0+o3FG zeqLdkDu{Uim(si-0`G3Hvmb$i_1j$E!v!?1Aifvi!^{f-K8k!0>!$;Jin*AFi1R0q z$LKxdN%Y!l7TvG-{5O0VIo&J1SDX(gXW4wtI6OQ{XWYJm$-<}H3Ixbr6SN)eaj@|T6y2UB1o0!wI<~E$C+RjC> s(h1lv@;oTc4PoSThtOq&>w3{rF1RW@r&(sym + entry.second.field_offset); u64 info = 0; info |= sym_indices[entry.second.symbol] << 32l; - info |= elf_reloc_for(arch, entry.second.type); + info |= elf_reloc_for(arch, entry.second.type, entry.second.symbol.type); rel.write(info); } diff --git a/jasmine/x64.cpp b/jasmine/x64.cpp index fd61491..adfe736 100644 --- a/jasmine/x64.cpp +++ b/jasmine/x64.cpp @@ -52,6 +52,10 @@ namespace x64 { return type >= REGISTER8 && type <= REGISTER64; } + bool is_register_offset(ArgType type) { + return (type >= REGISTER_OFFSET8 && type <= REGISTER_OFFSET64) || type == REGISTER_OFFSET_AUTO; + } + bool is_immediate(ArgType type) { return (type >= IMM8 && type <= IMM64) || type == IMM_AUTO; } @@ -708,8 +712,7 @@ namespace x64 { } else modrm |= 0b11000000; // register-register mod - if (disp && !is_displacement_only(src.type) && - !is_displacement_only(dest.type)) { + if (is_register_offset(src.type) || is_register_offset(dest.type)) { if (disp > -129 && disp < 128) modrm |= 0b01000000; // 8-bit offset else if (disp < -0x80000000l || disp > 0x7fffffffl) { fprintf(stderr, "[ERROR] Cannot represent memory offset %lx " @@ -729,7 +732,7 @@ namespace x64 { else if (is_scaled_addressing(src.type) || is_absolute(src.type)) modrm |= RSP; else if (is_rip_relative(src.type)) - modrm |= RBP; + modrm |= RBP, modrm |= (base_register(dest) & 7) << 3; else modrm |= (base_register(dest) & 7); // destination register in r/m byte target->code().write(modrm); @@ -738,7 +741,7 @@ namespace x64 { if (is_displacement_only(src.type) || is_displacement_only(dest.type)) { target->code().write(little_endian((i32)disp)); } - else if (disp) { + else if (is_register_offset(src.type) || is_register_offset(dest.type)) { if (disp > -129 && disp < 128) target->code().write((i8)disp); else target->code().write(little_endian((i32)disp)); } @@ -880,9 +883,15 @@ namespace x64 { if (actual_size != BYTE) opcode ++; // set bottom bit for non-8-bit mode if (is_memory(src.type)) opcode += 2; // set next lowest bit for memory source target->code().write(opcode); + + Arg realsrc = src; + if (is_label(src.type)) realsrc = riprel64(0); - if (is_memory(src.type)) emitargs(src, dest, actual_size); - else emitargs(dest, src, actual_size); + if (is_memory(src.type)) emitargs(realsrc, dest, actual_size); + else emitargs(dest, realsrc, actual_size); + + if (is_label(src.type)) + target->reference(src.data.label, relative(DWORD), -4); } } diff --git a/run-tests.sh b/run-tests.sh new file mode 100644 index 0000000..c0b5366 --- /dev/null +++ b/run-tests.sh @@ -0,0 +1,13 @@ +TEMP=".temp" + +for file in test/*.bl; do + cat $file | grep "# = .*" | cut -d " " -f 3 > $TEMP.1 + ./basil $file &> $TEMP.2 + if diff -q $TEMP.1 $TEMP.2 &> /dev/null; then + echo "Test '$file' passed!" + else + echo "Test '$file' failed. Diff:" + sdiff -sd $TEMP.1 $TEMP.2 + fi + rm $TEMP.1 $TEMP.2 +done \ No newline at end of file diff --git a/test/math.bl b/test/math.bl new file mode 100644 index 0000000..58eeb5c --- /dev/null +++ b/test/math.bl @@ -0,0 +1,27 @@ +# = 6 +display 1 + 2 + 3 + +# = 5 +display 1 * 2 + 3 +# = 7 +display 1 + 2 * 3 +# = 9 +display (1 + 2) * 3 +# = 7 +display 1 + (2 * 3) +# = 10 +display 2(2) + 2(-2 + 5) + +# = 3 +display 5 - 1 - 1 +# = 5 +display 5 - (1 - 1) +# = 7 +display 5 - -1 - -1 + +# = 5 +display 80 / 8 / 2 +# = -20 +display 80 / (-8 / 2) +# = 0 +display 20 / 21 diff --git a/test/rational.bl b/test/rational.bl new file mode 100644 index 0000000..f6ba997 --- /dev/null +++ b/test/rational.bl @@ -0,0 +1,8 @@ +def Rational Rational of (int, int) +def (display r?: Rational) + display r.0 + display "/" + display r.1 + +def x: Rational (1, 2) +display x \ No newline at end of file From 088c97a9984fb9629e27bf9db49040f6fd6db2ea Mon Sep 17 00:00:00 2001 From: elucent <9532786+elucent@users.noreply.github.com> Date: Thu, 11 Feb 2021 22:29:18 -0500 Subject: [PATCH 10/17] Misc. changes. Macros have been fixed, compile-time recursion works, bunch of other small stuff. --- compiler/builtin.cpp | 14 ++++++- compiler/builtin.h | 2 +- compiler/eval.cpp | 88 +++++++++++++++++++++++++++++++------------- compiler/lex.cpp | 2 +- compiler/type.cpp | 3 +- compiler/type.h | 2 +- compiler/values.cpp | 52 ++++++++++++++++++++++++++ compiler/values.h | 16 ++++++++ example/factorial.bl | 5 ++- test/rational.bl | 16 ++++++++ 10 files changed, 167 insertions(+), 33 deletions(-) diff --git a/compiler/builtin.cpp b/compiler/builtin.cpp index 11cf182..7a2b5a2 100644 --- a/compiler/builtin.cpp +++ b/compiler/builtin.cpp @@ -205,6 +205,18 @@ namespace basil { }, nullptr }; + AT_MODULE = { + find(find(MODULE, SYMBOL), ANY), + [](ref env, const Value& args) -> Value { + if (!ARG(0).get_module().has(ARG(1).get_symbol())) { + err(ARG(1).loc(), "Module does not contain member '", + symbol_for(ARG(1).get_symbol()), "'."); + return error(); + } + return ARG(0).get_module().entry(ARG(1).get_symbol()); + }, + nullptr + }; ANNOTATE = { find(find(ANY, TYPE), ANY), [](ref env, const Value& args) -> Value { @@ -283,7 +295,7 @@ namespace basil { env->infix("<=", Value(env, LESS_EQUAL), 2, 10); env->infix(">=", Value(env, GREATER_EQUAL), 2, 10); env->def("display", Value(env, DISPLAY), 1); - env->infix("at", cases(env, &AT_INT, &AT_LIST, &AT_ARRAY_TYPE, &AT_DYNARRAY_TYPE), 2, 120); + env->infix("at", cases(env, &AT_INT, &AT_LIST, &AT_ARRAY_TYPE, &AT_DYNARRAY_TYPE, &AT_MODULE), 2, 120); env->def("annotate", Value(env, ANNOTATE), 2); env->def("typeof", Value(env, TYPEOF), 1); env->infix_macro("of", Value(env, OF_TYPE_MACRO), 2, 20); diff --git a/compiler/builtin.h b/compiler/builtin.h index 527875e..edc5f72 100644 --- a/compiler/builtin.h +++ b/compiler/builtin.h @@ -38,7 +38,7 @@ namespace basil { EQUALS, NOT_EQUALS, LESS, GREATER, LESS_EQUAL, GREATER_EQUAL, IS_EMPTY, HEAD, TAIL, CONS, DISPLAY, READ_LINE, READ_WORD, READ_INT, - LENGTH, AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, STRCAT, SUBSTR, + LENGTH, AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, AT_MODULE, STRCAT, SUBSTR, ANNOTATE, TYPEOF, LIST_TYPE, OF_TYPE_MACRO, OF_TYPE, ASSIGN, IF; } diff --git a/compiler/eval.cpp b/compiler/eval.cpp index c9a6941..10467f1 100644 --- a/compiler/eval.cpp +++ b/compiler/eval.cpp @@ -578,21 +578,26 @@ namespace basil { return cons(op, cons(lhs, l)); } - pair unary_helper(ref env, const Value& lhs, const Value& term); - pair infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& rhs, + struct InfixResult { + Value first, second; + bool changed; + }; + + InfixResult unary_helper(ref env, const Value& lhs, const Value& term); + InfixResult infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& rhs, const Value& term, const vector& internals); - pair infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& term); - pair infix_transform(ref env, const Value& term); + InfixResult infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& term); + InfixResult infix_transform(ref env, const Value& term); - pair infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& rhs, + InfixResult infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& rhs, const Value& term, const vector& internals) { Value iter = term; - if (iter.is_void()) return {apply_op(op, lhs, internals, rhs), iter}; + if (iter.is_void()) return {apply_op(op, lhs, internals, rhs), iter, true}; Value next_op = head(iter); - if (!next_op.is_symbol()) return {apply_op(op, lhs, internals, rhs), iter}; + if (!next_op.is_symbol()) return {apply_op(op, lhs, internals, rhs), iter, true}; const Def* next_def = env->find(symbol_for(next_op.get_symbol())); - if (!next_def || !next_def->is_infix) return {apply_op(op, lhs, internals, rhs), iter}; + if (!next_def || !next_def->is_infix) return {apply_op(op, lhs, internals, rhs), iter, true}; iter = tail(iter); // consume op if (next_def->precedence > def->precedence) { @@ -600,7 +605,7 @@ namespace basil { return infix_helper(env, lhs, op, def, apply_op(next_op, rhs), iter, internals); } auto p = infix_helper(env, rhs, next_op, next_def, iter); - return {apply_op(op, lhs, internals, p.first), p.second}; + return {apply_op(op, lhs, internals, p.first), p.second, true}; } else { Value result = apply_op(op, lhs, rhs); if (next_def->arity == 1) { return unary_helper(env, apply_op(next_op, result), iter); } @@ -608,7 +613,7 @@ namespace basil { } } - pair infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, + InfixResult infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& term) { Value iter = term; @@ -622,7 +627,7 @@ namespace basil { if (iter.is_void()) { err(term.loc(), "Expected term in binary expression."); - return {error(), term}; + return {error(), term, false}; } Value rhs = head(iter); iter = tail(iter); // consume second term @@ -630,21 +635,21 @@ namespace basil { return infix_helper(env, lhs, op, def, rhs, iter, internals); } - pair unary_helper(ref env, const Value& lhs, const Value& term) { + InfixResult unary_helper(ref env, const Value& lhs, const Value& term) { Value iter = term; - if (iter.is_void()) return {lhs, iter}; + if (iter.is_void()) return {lhs, iter, false}; Value op = head(iter); - if (!op.is_symbol()) return {lhs, iter}; + if (!op.is_symbol()) return {lhs, iter, false}; const Def* def = env->find(symbol_for(op.get_symbol())); - if (!def || !def->is_infix) return {lhs, iter}; + if (!def || !def->is_infix) return {lhs, iter, false}; iter = tail(iter); // consume op if (def->arity == 1) return unary_helper(env, apply_op(op, lhs), iter); return infix_helper(env, lhs, op, def, iter); } - pair infix_transform(ref env, const Value& term) { + InfixResult infix_transform(ref env, const Value& term) { Value iter = term; if (iter.is_void()) { @@ -654,11 +659,11 @@ namespace basil { Value lhs = head(iter); // 1 + 2 -> 1 iter = tail(iter); // consume first term - if (iter.is_void()) return {lhs, iter}; + if (iter.is_void()) return {lhs, iter, false}; Value op = head(iter); - if (!op.is_symbol()) return {lhs, iter}; + if (!op.is_symbol()) return {lhs, iter, false}; const Def* def = env->find(symbol_for(op.get_symbol())); - if (!def || !def->is_infix) return {lhs, iter}; + if (!def || !def->is_infix) return {lhs, iter, false}; iter = tail(iter); // consume op if (def->arity == 1) return unary_helper(env, apply_op(op, lhs), iter); @@ -668,12 +673,14 @@ namespace basil { Value handle_infix(ref env, const Value& term) { vector infix_exprs; Value iter = term; + bool changed = false; while (iter.is_list()) { auto p = infix_transform(env, iter); infix_exprs.push(p.first); // next s-expr iter = p.second; // move past it in source list + changed = changed || p.changed; } - Value result = list_of(infix_exprs); + Value result = infix_exprs.size() == 1 && changed ? infix_exprs[0] : list_of(infix_exprs); return result; } @@ -694,7 +701,7 @@ namespace basil { Value h = head(item); if (h.is_symbol()) { const string& name = symbol_for(h.get_symbol()); - if (name == "macro" || name == "lambda" || name == "quote" || + if (name == "macro" || name == "lambda" || name == "splice") return; // else if (name == "def") { @@ -723,7 +730,7 @@ namespace basil { handle_macro(env, term); visit_defs(env, term); apply_infix(env, term); - define_procedures(env, term); + // define_procedures(env, term); } Value overload(ref env, Def* existing, const Value& func, const string& name) { @@ -1235,12 +1242,41 @@ namespace basil { static map modules; Value use(ref env, const Value& term) { - if (!tail(term).is_list() || !head(tail(term)).is_symbol()) { - err(tail(term).loc(), "Expected symbol in use expression, given '", tail(term), "'."); + if (!tail(term).is_list() || is_empty(tail(term))) { + err(tail(term).loc(), "Expected body in use expression."); return error(); } + + string path; Value h = head(tail(term)); - string path = symbol_for(h.get_symbol()); + // use + // use as + if (head(tail(term)).is_symbol()) { + path = symbol_for(h.get_symbol()); + } else { // use [ ... ] + if (!h.is_symbol() || symbol_for(h.get_symbol()) != "at") { + err(h.loc(), ""); + return error(); + } + if (!tail(h).is_list() || is_empty(tail(h)) || !head(tail(h)).is_symbol()) { + err(tail(h).loc(), ""); + return error(); + } + Value names = head(tail(tail(h))); + if (!names.is_list() || is_empty(names)) { + err(names.loc(), ""); + return error(); + } + + for (const Value &name : to_vector(names)) { + if (!name.is_symbol()) { + err(name.loc(), "Expected symbol in use, given'", name, "'"); + return error(); + } + // todo: finish + } + } + path += ".bl"; ref module; @@ -1277,7 +1313,7 @@ namespace basil { const string& name = symbol_for(h.get_symbol()); if (name == "quote") return head(tail(term)).clone(); else if (name == "def") - return define(env, term, false, true); + return define(env, term, false); else if (name == "macro") return define(env, term, true); else if (name == "lambda") diff --git a/compiler/lex.cpp b/compiler/lex.cpp index 3e9201a..4e20f1d 100644 --- a/compiler/lex.cpp +++ b/compiler/lex.cpp @@ -101,7 +101,7 @@ namespace basil { else if (isdelimiter(ch)) { view.read(); TokenType type = DELIMITERS[ch]; - if (DELIMITERS[ch] == T_LBRACK && !follows_space) type = T_ACCESS; + if (DELIMITERS[ch] == T_LBRACK && !follows_space && start_col > 0) type = T_ACCESS; return Token(type, { 1, start }, line, start_col); } else if (issymbolstart(ch)) { diff --git a/compiler/type.cpp b/compiler/type.cpp index 74f546f..2f5178e 100644 --- a/compiler/type.cpp +++ b/compiler/type.cpp @@ -562,7 +562,8 @@ namespace basil { *ALIAS = find(), *BOOL = find("bool"), *ANY = find("any"), - *STRING = find("string"); + *STRING = find("string"), + *MODULE = find("module"); const Type* unify(const Type* a, const Type* b, bool coercing, bool converting) { if (!a || !b) return nullptr; diff --git a/compiler/type.h b/compiler/type.h index 7d34404..c19daa5 100644 --- a/compiler/type.h +++ b/compiler/type.h @@ -236,7 +236,7 @@ namespace basil { } extern const Type *INT, *FLOAT, *SYMBOL, *VOID, *ERROR, *TYPE, - *ALIAS, *BOOL, *ANY, *STRING; + *ALIAS, *BOOL, *ANY, *STRING, *MODULE; const Type* unify(const Type* a, const Type* b, bool coercing = false, bool converting = false); } diff --git a/compiler/values.cpp b/compiler/values.cpp index bc957a8..5d2b588 100644 --- a/compiler/values.cpp +++ b/compiler/values.cpp @@ -97,6 +97,10 @@ namespace basil { _data.rc = n; } + Value::Value(ModuleValue* m) : _type(MODULE) { + _data.rc = m; + } + Value::Value(ASTNode* n) : _type(n->type()->kind() == KIND_RUNTIME ? n->type() : find(n->type())) { _data.rc = n; @@ -308,6 +312,18 @@ namespace basil { return *(NamedValue*)_data.rc; } + bool Value::is_module() const { + return _type == MODULE; + } + + const ModuleValue& Value::get_module() const { + return *(const ModuleValue*)_data.rc; + } + + ModuleValue& Value::get_module() { + return *(ModuleValue*)_data.rc; + } + const Type* Value::type() const { return _type; } @@ -372,6 +388,8 @@ namespace basil { write(io, "<#macro>"); else if (is_runtime()) write(io, "<#runtime ", ((const RuntimeType*)_type)->base(), ">"); + else if (is_module()) + write(io, "<#module>"); } u64 Value::hash() const { @@ -432,6 +450,12 @@ namespace basil { return h; } else if (is_runtime()) { return _type->hash() ^ ::hash(_data.rc); + } else if (is_module()) { + u64 h = 6343561091602366673ul; + for (auto& p : get_module().entries()) { + h ^= 12407217216741519607ul * ::hash(p.first); + h ^= p.second.hash(); + } } return 0; } @@ -497,6 +521,15 @@ namespace basil { } } else if (is_runtime()) { return _data.rc == other._data.rc; + } else if (is_module()) { + if (get_module().entries().size() != other.get_module().entries().size()) + return false; + for (auto& p : get_module().entries()) { + auto it = other.get_module().entries().find(p.first); + if (it == other.get_module().entries().end()) return false; + if (it->second != p.second) return false; + } + return true; } return type() == other.type(); } @@ -549,6 +582,10 @@ namespace basil { } } else if (is_runtime()) { // todo: ast cloning + } else if (is_module()) { + map members; + for (auto& p : get_module().entries()) members[p.first] = p.second.clone(); + return new ModuleValue(members); } return *this; } @@ -857,6 +894,21 @@ namespace basil { return _code; } + ModuleValue::ModuleValue(const map& entries): + _entries(entries) {} + + const map& ModuleValue::entries() const { + return _entries; + } + + bool ModuleValue::has(u64 name) const { + return _entries.find(name) != _entries.end(); + } + + const Value& ModuleValue::entry(u64 name) const { + return _entries[name]; + } + vector to_vector(const Value& list) { vector values; const Value* v = &list; diff --git a/compiler/values.h b/compiler/values.h index ac3bd8d..5586607 100644 --- a/compiler/values.h +++ b/compiler/values.h @@ -27,6 +27,7 @@ namespace basil { class AliasValue; class MacroValue; class NamedValue; + class ModuleValue; class Value { const Type* _type; @@ -57,6 +58,7 @@ namespace basil { Value(AliasValue* f); Value(MacroValue* f); Value(NamedValue* n, const Type* t); + Value(ModuleValue* m); Value(ASTNode* n); ~Value(); Value(const Value& other); @@ -130,6 +132,10 @@ namespace basil { const NamedValue& get_named() const; NamedValue& get_named(); + bool is_module() const; + const ModuleValue& get_module() const; + ModuleValue& get_module(); + const Type* type() const; void format(stream& io) const; u64 hash() const; @@ -281,6 +287,16 @@ namespace basil { const ref get_env() const; }; + class ModuleValue : public RC { + map _entries; + public: + ModuleValue(const map& entries); + + const map& entries() const; + bool has(u64 name) const; + const Value& entry(u64 name) const; + }; + Value lower(const Value& v); Value add(const Value& lhs, const Value& rhs); diff --git a/example/factorial.bl b/example/factorial.bl index cc739a7..1f176a3 100644 --- a/example/factorial.bl +++ b/example/factorial.bl @@ -1,3 +1,4 @@ -use std/math +def (n? factorial) + if n == 0 1 else n - 1 factorial * n -display 10 factorial \ No newline at end of file +display 100 factorial \ No newline at end of file diff --git a/test/rational.bl b/test/rational.bl index f6ba997..755b366 100644 --- a/test/rational.bl +++ b/test/rational.bl @@ -4,5 +4,21 @@ def (display r?: Rational) display "/" display r.1 + r . 0 + (at r 0) + at :: any[] -> int -> any + at :: (any tuple) -> int -> any + + int, int, int + Pair(int, Pair(int, int)) + at :: (any, any) -> int -> any + any-tuple + +def (variadic x?: int... y?: string) + (int..., string) + (int, string) + (int, int, string) + (string) + def x: Rational (1, 2) display x \ No newline at end of file From 104969a8e3d8a39c9defc64d0040ac7e699c5f1c Mon Sep 17 00:00:00 2001 From: elucent <9532786+elucent@users.noreply.github.com> Date: Wed, 17 Feb 2021 20:19:39 -0500 Subject: [PATCH 11/17] Tweaked makefile slightly. --- Makefile | 16 ++++++++-------- compiler/builtin.cpp | 2 +- compiler/eval.cpp | 42 +++++++++++++++++++++--------------------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/Makefile b/Makefile index 7ed9ff1..a7778d9 100644 --- a/Makefile +++ b/Makefile @@ -6,27 +6,27 @@ OBJS := $(patsubst %.cpp,%.o,$(SRCS)) CXX := clang++ CXXHEADERS := -I. -Iutil -Ijasmine -Icompiler -CXXFLAGS := $(CXXHEADERS) -std=c++17 -ffast-math -fno-rtti -fno-exceptions -Wno-null-dereference -LDFLAGS := -Wl,--unresolved-symbols=ignore-in-object-files +CXXFLAGS := $(CXXHEADERS) -std=c++17 -ffunction-sections -fdata-sections -ffast-math -fno-rtti -fno-exceptions -Wno-null-dereference +LDFLAGS := -Wl,--gc-sections -Wl,--unresolved-symbols=ignore-in-object-files clean: - rm -f $(OBJS) *.o.tmp basil + rm -f $(OBJS) *.o.tmp basil librt.a runtime/*.o basil: CXXFLAGS += -g3 release: CXXFLAGS += -Os -basil: $(OBJS) - $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ +basil: $(OBJS) librt.a + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $@ -release: $(OBJS) - $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o basil +release: $(OBJS) librt.a + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o basil librt.a: runtime/core.o runtime/sys.o ar r $@ $^ runtime/%.o: runtime/%.cpp - $(CXX) -I. -Iutil -std=c++11 -fPIC -Os -ffast-math -fno-rtti -fno-exceptions -Os -nostdlib -nostdlib++ -c $< -o $@ + $(CXX) -I. -Iutil -std=c++11 -fPIC -ffast-math -fno-rtti -fno-exceptions -Os -nostdlib -nostdlib++ -c $< -o $@ %.o: %.cpp %.h $(CXX) $(CXXFLAGS) -c $< -o $@ diff --git a/compiler/builtin.cpp b/compiler/builtin.cpp index 7a2b5a2..72b5d67 100644 --- a/compiler/builtin.cpp +++ b/compiler/builtin.cpp @@ -37,7 +37,7 @@ namespace basil { AND, OR, XOR, NOT, EQUALS, NOT_EQUALS, LESS, GREATER, LESS_EQUAL, GREATER_EQUAL, IS_EMPTY, HEAD, TAIL, CONS, DISPLAY, READ_LINE, READ_WORD, READ_INT, LENGTH, - AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, STRCAT, SUBSTR, ANNOTATE, TYPEOF, LIST_TYPE, OF_TYPE_MACRO, + AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, AT_MODULE, STRCAT, SUBSTR, ANNOTATE, TYPEOF, LIST_TYPE, OF_TYPE_MACRO, OF_TYPE, ASSIGN, IF; static void init_builtins() { diff --git a/compiler/eval.cpp b/compiler/eval.cpp index 10467f1..e060805 100644 --- a/compiler/eval.cpp +++ b/compiler/eval.cpp @@ -1242,10 +1242,10 @@ namespace basil { static map modules; Value use(ref env, const Value& term) { - if (!tail(term).is_list() || is_empty(tail(term))) { - err(tail(term).loc(), "Expected body in use expression."); - return error(); - } + // if (!tail(term).is_list() || is_empty(tail(term))) { + // err(tail(term).loc(), "Expected body in use expression."); + // return error(); + // } string path; Value h = head(tail(term)); @@ -1258,23 +1258,23 @@ namespace basil { err(h.loc(), ""); return error(); } - if (!tail(h).is_list() || is_empty(tail(h)) || !head(tail(h)).is_symbol()) { - err(tail(h).loc(), ""); - return error(); - } - Value names = head(tail(tail(h))); - if (!names.is_list() || is_empty(names)) { - err(names.loc(), ""); - return error(); - } - - for (const Value &name : to_vector(names)) { - if (!name.is_symbol()) { - err(name.loc(), "Expected symbol in use, given'", name, "'"); - return error(); - } - // todo: finish - } + // if (!tail(h).is_list() || is_empty(tail(h)) || !head(tail(h)).is_symbol()) { + // err(tail(h).loc(), ""); + // return error(); + // } + // Value names = head(tail(tail(h))); + // if (!names.is_list() || is_empty(names)) { + // err(names.loc(), ""); + // return error(); + // } + + // for (const Value &name : to_vector(names)) { + // if (!name.is_symbol()) { + // err(name.loc(), "Expected symbol in use, given'", name, "'"); + // return error(); + // } + // // todo: finish + // } } path += ".bl"; From b8ccbb18cb1a95eb75f789dc838180cbb1f3a90c Mon Sep 17 00:00:00 2001 From: Alec Minchington Date: Wed, 17 Feb 2021 21:42:07 -0500 Subject: [PATCH 12/17] New module import system --- compiler/builtin.cpp | 26 ++++---- compiler/env.cpp | 4 ++ compiler/env.h | 1 + compiler/eval.cpp | 150 ++++++++++++++++++++++++++----------------- 4 files changed, 109 insertions(+), 72 deletions(-) diff --git a/compiler/builtin.cpp b/compiler/builtin.cpp index 7a2b5a2..47787f8 100644 --- a/compiler/builtin.cpp +++ b/compiler/builtin.cpp @@ -33,11 +33,11 @@ namespace basil { #define ARG(n) args.get_product()[n] - Builtin ADD_INT, ADD_SYMBOL, SUB, MUL, DIV, REM, - AND, OR, XOR, NOT, + Builtin ADD_INT, ADD_SYMBOL, SUB, MUL, DIV, REM, + AND, OR, XOR, NOT, EQUALS, NOT_EQUALS, LESS, GREATER, LESS_EQUAL, GREATER_EQUAL, - IS_EMPTY, HEAD, TAIL, CONS, DISPLAY, READ_LINE, READ_WORD, READ_INT, LENGTH, - AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, STRCAT, SUBSTR, ANNOTATE, TYPEOF, LIST_TYPE, OF_TYPE_MACRO, + IS_EMPTY, HEAD, TAIL, CONS, DISPLAY, READ_LINE, READ_WORD, READ_INT, LENGTH, + AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, AT_MODULE, STRCAT, SUBSTR, ANNOTATE, TYPEOF, LIST_TYPE, OF_TYPE_MACRO, OF_TYPE, ASSIGN, IF; static void init_builtins() { @@ -47,8 +47,8 @@ namespace basil { return Value(new ASTBinaryMath(ARG(0).loc(), AST_ADD, ARG(0).get_runtime(), ARG(1).get_runtime())); }}; ADD_SYMBOL = {find(find(SYMBOL, SYMBOL), SYMBOL), - [](ref env, const Value& args) -> Value { - return Value(symbol_for(ARG(0).get_symbol()) + symbol_for(ARG(1).get_symbol()), SYMBOL); + [](ref env, const Value& args) -> Value { + return Value(symbol_for(ARG(0).get_symbol()) + symbol_for(ARG(1).get_symbol()), SYMBOL); }, nullptr}; SUB = {find(find(INT, INT), INT), @@ -139,7 +139,7 @@ namespace basil { } }; DISPLAY = { - find(find(ANY), VOID), + find(find(ANY), VOID), nullptr, [](ref env, const Value& args) -> Value { return Value(new ASTDisplay(ARG(0).loc(), ARG(0).get_runtime())); @@ -209,7 +209,7 @@ namespace basil { find(find(MODULE, SYMBOL), ANY), [](ref env, const Value& args) -> Value { if (!ARG(0).get_module().has(ARG(1).get_symbol())) { - err(ARG(1).loc(), "Module does not contain member '", + err(ARG(1).loc(), "Module does not contain member '", symbol_for(ARG(1).get_symbol()), "'."); return error(); } @@ -221,7 +221,7 @@ namespace basil { find(find(ANY, TYPE), ANY), [](ref env, const Value& args) -> Value { if (!ARG(0).type()->coerces_to(ARG(1).get_type())) { - err(ARG(0).loc(), "Could not unify value of type '", ARG(0).type(), "' with type '", + err(ARG(0).loc(), "Could not unify value of type '", ARG(0).type(), "' with type '", ARG(1).get_type(), "'."); return error(); } @@ -240,15 +240,15 @@ namespace basil { nullptr}; OF_TYPE_MACRO = { find(2), - [](ref env, const Value& args) -> Value { - return list_of(Value("#of", SYMBOL), list_of(Value("quote", SYMBOL), ARG(0)), ARG(1)); + [](ref env, const Value& args) -> Value { + return list_of(Value("#of", SYMBOL), list_of(Value("quote", SYMBOL), ARG(0)), ARG(1)); }, nullptr }; OF_TYPE = { find(find(SYMBOL, TYPE), TYPE), - [](ref env, const Value& args) -> Value { - return Value(find(symbol_for(ARG(0).get_symbol()), ARG(1).get_type()), TYPE); + [](ref env, const Value& args) -> Value { + return Value(find(symbol_for(ARG(0).get_symbol()), ARG(1).get_type()), TYPE); }, nullptr }; diff --git a/compiler/env.cpp b/compiler/env.cpp index 9fcd078..388044c 100644 --- a/compiler/env.cpp +++ b/compiler/env.cpp @@ -122,6 +122,10 @@ namespace basil { for (auto& p : env->_defs) _defs.put(p.first, p.second); } + void Env::import_single(const string &name, const Def &d) { + _defs.put(name, d); + } + void Env::make_runtime() { _runtime = true; } diff --git a/compiler/env.h b/compiler/env.h index d406edc..9c6b405 100644 --- a/compiler/env.h +++ b/compiler/env.h @@ -57,6 +57,7 @@ namespace basil { map::const_iterator begin() const; map::const_iterator end() const; void import(ref env); + void import_single(const string &name, const Def &d); void make_runtime(); bool is_runtime() const; }; diff --git a/compiler/eval.cpp b/compiler/eval.cpp index 10467f1..a50ffcb 100644 --- a/compiler/eval.cpp +++ b/compiler/eval.cpp @@ -370,8 +370,9 @@ namespace basil { } bool is_valid_infix_def(const Value& term) { - return term.is_list() && tail(term).is_list() && is_valid_argument(head(term)) && is_keyword(head(tail(term))) - || is_annotation(term) && tail(term).is_list() && is_valid_infix_def(head(tail(term))); + return term.is_list() && tail(term).is_list() && is_valid_argument(head(term)) && + is_keyword(head(tail(term))) || + is_annotation(term) && tail(term).is_list() && is_valid_infix_def(head(tail(term))); } string get_variable_name(const Value& term) { @@ -444,8 +445,7 @@ namespace basil { // return; // } if (!env->find(name)) env->def(name); - } - else if (is_valid_def(values[i])) { // procedure + } else if (is_valid_def(values[i])) { // procedure const string& name = get_def_name(values[i]); if (name.endswith('?')) { err(values[i].loc(), "Invalid name for procedure: cannot end in '?'."); @@ -464,8 +464,7 @@ namespace basil { // return; // } if (!env->find(name)) env->def(name, to_vector(tail(values[i])).size()); - } - else if (is_valid_infix_def(values[i])) { // infix procedure + } else if (is_valid_infix_def(values[i])) { // infix procedure Value rest = tail(values[i]); if (!rest.is_list()) { err(rest.loc(), "Infix procedure must take at least one ", "argument."); @@ -481,22 +480,21 @@ namespace basil { return; } if (!env->find(name)) env->infix(name, to_vector(tail(rest)).size() + 1, precedence); - } - else { + } else { err(item.loc(), "Invalid definition."); return; } - } - else traverse_list(env, item, visit_defs); + } else + traverse_list(env, item, visit_defs); } - + void define_procedures(ref env, const Value& item) { if (!item.is_list()) return; Value h = head(item); if (symbol_matches(h, "def")) { define(env, item, false, false, true); - } - else traverse_list(env, item, define_procedures); + } else + traverse_list(env, item, define_procedures); } void handle_macro(ref env, Value& item); @@ -585,12 +583,12 @@ namespace basil { InfixResult unary_helper(ref env, const Value& lhs, const Value& term); InfixResult infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& rhs, - const Value& term, const vector& internals); + const Value& term, const vector& internals); InfixResult infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& term); InfixResult infix_transform(ref env, const Value& term); InfixResult infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& rhs, - const Value& term, const vector& internals) { + const Value& term, const vector& internals) { Value iter = term; if (iter.is_void()) return {apply_op(op, lhs, internals, rhs), iter, true}; @@ -613,8 +611,7 @@ namespace basil { } } - InfixResult infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, - const Value& term) { + InfixResult infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& term) { Value iter = term; vector internals; @@ -701,12 +698,10 @@ namespace basil { Value h = head(item); if (h.is_symbol()) { const string& name = symbol_for(h.get_symbol()); - if (name == "macro" || name == "lambda" || - name == "splice") - return; + if (name == "macro" || name == "lambda" || name == "splice") return; // else if (name == "def") { // if (tail(item).is_list() && head(tail(item)).is_symbol()) apply_infix_at(env, item, 2); - // } + // } else if (name == "if" || name == "do" || name == "list-of") apply_infix_at(env, item, 1); else { @@ -767,8 +762,7 @@ namespace basil { Value(new IntersectValue(values), find(existing->value.type(), func.type())); return Value(VOID); } else { - err(func.loc(), "Cannot redefine symbol '", name, "' of type '", existing->value.type(), - "' as function."); + err(func.loc(), "Cannot redefine symbol '", name, "' of type '", existing->value.type(), "' as function."); return error(); } } @@ -816,7 +810,8 @@ namespace basil { ref function_env = newref(env); vector args; vector elts = to_vector(get_def_info(values[i])); - for (u32 i = 0; i < elts.size(); i ++) if (i != infix ? 1 : 0) args.push(elts[i]); + for (u32 i = 0; i < elts.size(); i++) + if (i != infix ? 1 : 0) args.push(elts[i]); vector argnames; vector body; vector argts; @@ -853,7 +848,7 @@ namespace basil { } else if (!is_keyword(v)) argts.push(ANY); } - for (u32 j = i + 1; j < values.size(); j ++) body.push(values[j]); + for (u32 j = i + 1; j < values.size(); j++) body.push(values[j]); Value body_term = cons(Value("do"), list_of(body)); if (is_macro) { if (is_annotation(values[i])) { @@ -862,7 +857,8 @@ namespace basil { } Value mac(new MacroValue(function_env, argnames, body_term)); if (infix) env->infix_macro(name, mac, argnames.size(), precedence); - else env->def_macro(name, mac, argnames.size()); + else + env->def_macro(name, mac, argnames.size()); } else { const Type* returntype = ANY; if (is_annotation(values[i])) { @@ -890,19 +886,18 @@ namespace basil { const FunctionType* ft = (const FunctionType*)find(argst, returntype); u64 fname = symbol_value(name); Value func = external ? Value(new ASTFunction(term.loc(), function_env, argst, argnames, - new ASTExtern(term.loc(), returntype), fname)) - : Value(new FunctionValue(function_env, argnames, body_term, fname), ft); + new ASTExtern(term.loc(), returntype), fname)) + : Value(new FunctionValue(function_env, argnames, body_term, fname), ft); Def* existing = env->find(name); if (existing && !existing->value.is_void()) { return overload(env, existing, func, name); } else if (infix) { env->infix(name, func, argnames.size(), precedence); - } else env->def(name, func, argnames.size()); - if (func.is_runtime()) - return new ASTDefine(values[0].loc(), env, fname, func.get_runtime()); + } else + env->def(name, func, argnames.size()); + if (func.is_runtime()) return new ASTDefine(values[0].loc(), env, fname, func.get_runtime()); if (argst->concrete() && !external) { - func.get_function().instantiate(argst, - new ASTIncompleteFn(func.loc(), argst, symbol_value(name))); + func.get_function().instantiate(argst, new ASTIncompleteFn(func.loc(), argst, symbol_value(name))); instantiate(func.loc(), func.get_function(), find(argts)); } } @@ -1012,7 +1007,8 @@ namespace basil { return error(); } return overload(env, existing, func, name); - } else env->infix(name, func, argnames.size(), precedence); + } else + env->infix(name, func, argnames.size(), precedence); // if (argst->concrete()) instantiate(func.loc(), func.get_function(), find(argts)); } return Value(VOID); @@ -1242,39 +1238,67 @@ namespace basil { static map modules; Value use(ref env, const Value& term) { - if (!tail(term).is_list() || is_empty(tail(term))) { + if (!tail(term).is_list() || tail(term).is_void()) { err(tail(term).loc(), "Expected body in use expression."); return error(); } - - string path; + + string path, module_name; + set names; Value h = head(tail(term)); - // use - // use as + // use -> introduce a modulevalue with name into the env + // use as -> introduce a modulevalue with name into the env if (head(tail(term)).is_symbol()) { - path = symbol_for(h.get_symbol()); - } else { // use [ ... ] - if (!h.is_symbol() || symbol_for(h.get_symbol()) != "at") { - err(h.loc(), ""); + if (!h.is_symbol()) { + err(h.loc(), "Expected module name in use expression, got '", h, "'"); return error(); } - if (!tail(h).is_list() || is_empty(tail(h)) || !head(tail(h)).is_symbol()) { - err(tail(h).loc(), ""); + + path = symbol_for(h.get_symbol()); + + if (!tail(tail(term)).is_void() && tail(tail(term)).is_list()) { // use as + Value as_exp = tail(tail(term)); + if (!head(as_exp).is_symbol() || symbol_for(head(as_exp).get_symbol()) != "as") { + err(head(as_exp).loc(), "Expected 'as' after module name in use expression, got '", head(as_exp), + "'"); + return error(); + } + + if (tail(as_exp).is_void() || !head(tail(as_exp)).is_symbol()) { + err(tail(as_exp).loc(), "Expected symbol after 'as' in use expression"); + return error(); + } + module_name = symbol_for(head(tail(as_exp)).get_symbol()); + } else { + module_name = path; + } + } else { // use [ ... ] -> introduce names from [ ... ] into the env + if (!head(h).is_symbol() || symbol_for(head(h).get_symbol()) != "at") { + err(h.loc(), "Expected at-expression in use, got '", head(h), "'"); return error(); } - Value names = head(tail(tail(h))); - if (!names.is_list() || is_empty(names)) { - err(names.loc(), ""); + if (!tail(h).is_list() || tail(h).is_void() || !head(tail(h)).is_symbol()) { + err(tail(h).loc(), "Expected body in at-expression in use"); return error(); } - - for (const Value &name : to_vector(names)) { - if (!name.is_symbol()) { - err(name.loc(), "Expected symbol in use, given'", name, "'"); + Value nameslist = head(tail(tail(h))); // May be either a list of names or just a single symbol + if (!nameslist.is_list()) { + if (!nameslist.is_symbol()) { + err(term.loc(), "Expected name(s) inside of brackets in bracketed use expression"); return error(); } - // todo: finish + names.insert(symbol_for(nameslist.get_symbol())); + } else { + nameslist = tail(nameslist); + for (const Value& name : to_vector(nameslist)) { + if (!name.is_symbol()) { + err(name.loc(), "Expected symbol in use expression, given'", name, "'"); + return error(); + } + names.insert(symbol_for(name.get_symbol())); + } } + path = symbol_for(head(tail(h)).get_symbol()); } path += ".bl"; @@ -1292,18 +1316,26 @@ namespace basil { module = unit.env = load(unit.source); if (error_count()) return error(); - for (const auto& p : *module) { - if (env->find(p.first)) { - err(term.loc(), "Module '", symbol_for(h.get_symbol()), "' redefines '", p.first, - "' in the current environment."); - return error(); + if (names.size()) { // use [names...] + for (const auto& p : *module) { + if (names.find(p.first) != names.end()) { + if (env->find(p.first)) { + err(term.loc(), "Module '", symbol_for(h.get_symbol()), "' redefines '", p.first, + "' in the current environment."); + return error(); + } + env->import_single(p.first, p.second); + } } + } else { // use | use as + map values; + for (const auto& p : *module) values.put(symbol_value(p.first), p.second.value); + env->def(module_name, Value(new ModuleValue(values))); } modules.put(path, unit); } - env->import(module); return Value(VOID); } From e33d3533cc971763ed210d77f99d48ff85432cec Mon Sep 17 00:00:00 2001 From: Alec Minchington Date: Fri, 19 Feb 2021 00:01:16 -0500 Subject: [PATCH 13/17] Added typedef. --- compiler/builtin.cpp | 10 +++++++++- compiler/builtin.h | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/compiler/builtin.cpp b/compiler/builtin.cpp index 9eff589..698cf4a 100644 --- a/compiler/builtin.cpp +++ b/compiler/builtin.cpp @@ -46,7 +46,7 @@ namespace basil { IS_EMPTY, HEAD, TAIL, CONS, DICT_IN, DICT_NOT_IN, DISPLAY, READ_LINE, READ_WORD, READ_INT, LENGTH, AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, AT_MODULE, AT_DICT, AT_DICT_TYPE, AT_DICT_LIST, - STRCAT, SUBSTR, ANNOTATE, TYPEOF, LIST_TYPE, OF_TYPE_MACRO, + STRCAT, SUBSTR, ANNOTATE, TYPEOF, TYPEDEF, LIST_TYPE, OF_TYPE_MACRO, OF_TYPE, ASSIGN, IF; static void init_builtins() { @@ -281,6 +281,13 @@ namespace basil { }; TYPEOF = {find(find(ANY), TYPE), [](ref env, const Value& args) -> Value { return Value(ARG(0).type(), TYPE); }, nullptr}; + TYPEDEF = { + find(2), + [](ref env, const Value &args) -> Value { + return list_of(Value("def", SYMBOL), ARG(0), list_of(Value("#of", SYMBOL), list_of(Value("quote", SYMBOL), ARG(0)), ARG(1))); + }, + nullptr + }; LIST_TYPE = { find(find(TYPE), TYPE), [](ref env, const Value& args) -> Value { return Value(find(ARG(0).get_type()), TYPE); }, @@ -348,6 +355,7 @@ namespace basil { &AT_MODULE, &AT_DICT, &AT_DICT_TYPE, &AT_DICT_LIST), 2, 120); env->def("annotate", Value(env, ANNOTATE), 2); env->def("typeof", Value(env, TYPEOF), 1); + env->def_macro("typedef", Value(env, TYPEDEF), 2); env->infix_macro("of", Value(env, OF_TYPE_MACRO), 2, 20); env->def("#of", Value(env, OF_TYPE), 2); env->infix("list", Value(env, LIST_TYPE), 1, 80); diff --git a/compiler/builtin.h b/compiler/builtin.h index 02031ac..9cc9dea 100644 --- a/compiler/builtin.h +++ b/compiler/builtin.h @@ -44,7 +44,7 @@ namespace basil { IS_EMPTY, HEAD, TAIL, CONS, DICT_PUT, DICT_IN, DICT_NOT_IN, DICT_REMOVE, DISPLAY, READ_LINE, READ_WORD, READ_INT, LENGTH, AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, AT_DICT_TYPE, AT_MODULE, AT_DICT, AT_DICT_LIST, - STRCAT, SUBSTR, ANNOTATE, TYPEOF, LIST_TYPE, OF_TYPE_MACRO, OF_TYPE, + STRCAT, SUBSTR, ANNOTATE, TYPEOF, TYPEDEF, LIST_TYPE, OF_TYPE_MACRO, OF_TYPE, ASSIGN, IF; } From d6ef7779ee0326ae66ef1b973af160398d21cc1f Mon Sep 17 00:00:00 2001 From: Alec Minchington Date: Fri, 19 Feb 2021 00:06:25 -0500 Subject: [PATCH 14/17] Autoformatted all compiler code. --- compiler/ast.cpp | 1894 ++++++++++++++++++++---------------------- compiler/ast.h | 921 ++++++++++---------- compiler/builtin.cpp | 248 +++--- compiler/builtin.h | 28 +- compiler/driver.cpp | 579 +++++++------ compiler/driver.h | 26 +- compiler/env.cpp | 259 +++--- compiler/env.h | 104 +-- compiler/errors.cpp | 108 ++- compiler/errors.h | 68 +- compiler/eval.h | 12 +- compiler/ir.cpp | 1465 ++++++++++++++++---------------- compiler/ir.h | 889 ++++++++++---------- compiler/lex.cpp | 303 ++++--- compiler/lex.h | 98 ++- compiler/main.cpp | 1063 ++++++++++++------------ compiler/native.cpp | 399 ++++----- compiler/native.h | 10 +- compiler/ops.cpp | 284 +++---- compiler/ops.h | 6 +- compiler/parse.h | 9 +- compiler/source.cpp | 242 +++--- compiler/source.h | 74 +- compiler/ssa.cpp | 153 ++-- compiler/ssa.h | 139 ++-- compiler/type.cpp | 1343 +++++++++++++++--------------- compiler/type.h | 518 ++++++------ compiler/values.cpp | 110 ++- compiler/values.h | 774 ++++++++--------- 29 files changed, 5961 insertions(+), 6165 deletions(-) diff --git a/compiler/ast.cpp b/compiler/ast.cpp index b9f98ef..299893c 100644 --- a/compiler/ast.cpp +++ b/compiler/ast.cpp @@ -1,1028 +1,948 @@ #include "ast.h" -#include "values.h" #include "env.h" #include "type.h" +#include "values.h" namespace basil { - ASTNode::ASTNode(SourceLocation loc): - _loc(loc), _type(nullptr) {} + ASTNode::ASTNode(SourceLocation loc) : _loc(loc), _type(nullptr) {} - ASTNode::~ASTNode() {} + ASTNode::~ASTNode() {} - SourceLocation ASTNode::loc() const { - return _loc; - } + SourceLocation ASTNode::loc() const { + return _loc; + } - const Type* ASTNode::type() { - if (!_type) _type = lazy_type(); - return _type->concretify(); - } + const Type* ASTNode::type() { + if (!_type) _type = lazy_type(); + return _type->concretify(); + } - bool ASTNode::is_extern() const { - return false; - } + bool ASTNode::is_extern() const { + return false; + } - ASTSingleton::ASTSingleton(const Type* type): - ASTNode(NO_LOCATION), _type(type) {} + ASTSingleton::ASTSingleton(const Type* type) : ASTNode(NO_LOCATION), _type(type) {} - const Type* ASTSingleton::lazy_type() { - return _type; - } + const Type* ASTSingleton::lazy_type() { + return _type; + } - ref ASTSingleton::emit(ref& parent) { - return nullptr; - } + ref ASTSingleton::emit(ref& parent) { + return nullptr; + } - Location ASTSingleton::emit(Function& func) { - return loc_none(); - } + Location ASTSingleton::emit(Function& func) { + return loc_none(); + } - void ASTSingleton::format(stream& io) const { - write(io, ""); - } + void ASTSingleton::format(stream& io) const { + write(io, ""); + } - ASTVoid::ASTVoid(SourceLocation loc): - ASTNode(loc) {} + ASTVoid::ASTVoid(SourceLocation loc) : ASTNode(loc) {} - const Type* ASTVoid::lazy_type() { - return VOID; - } + const Type* ASTVoid::lazy_type() { + return VOID; + } - ref ASTVoid::emit(ref& parent) { - return ref(parent); - } + ref ASTVoid::emit(ref& parent) { + return ref(parent); + } - Location ASTVoid::emit(Function& function) { - return loc_immediate(0); - } + Location ASTVoid::emit(Function& function) { + return loc_immediate(0); + } - void ASTVoid::format(stream& io) const { - write(io, "[]"); - } + void ASTVoid::format(stream& io) const { + write(io, "[]"); + } - ASTInt::ASTInt(SourceLocation loc, i64 value): - ASTNode(loc), _value(value) {} + ASTInt::ASTInt(SourceLocation loc, i64 value) : ASTNode(loc), _value(value) {} - const Type* ASTInt::lazy_type() { - return INT; - } + const Type* ASTInt::lazy_type() { + return INT; + } - ref ASTInt::emit(ref& parent) { - return newref(parent, _value); - } + ref ASTInt::emit(ref& parent) { + return newref(parent, _value); + } - Location ASTInt::emit(Function& func) { - return loc_immediate(_value); - } + Location ASTInt::emit(Function& func) { + return loc_immediate(_value); + } - void ASTInt::format(stream& io) const { - write(io, _value); - } + void ASTInt::format(stream& io) const { + write(io, _value); + } - ASTSymbol::ASTSymbol(SourceLocation loc, u64 value): - ASTNode(loc), _value(value) {} + ASTSymbol::ASTSymbol(SourceLocation loc, u64 value) : ASTNode(loc), _value(value) {} - const Type* ASTSymbol::lazy_type() { - return SYMBOL; - } + const Type* ASTSymbol::lazy_type() { + return SYMBOL; + } - ref ASTSymbol::emit(ref& parent) { - return newref(parent, _value); - } + ref ASTSymbol::emit(ref& parent) { + return newref(parent, _value); + } - Location ASTSymbol::emit(Function& func) { - return loc_immediate(_value); - } + Location ASTSymbol::emit(Function& func) { + return loc_immediate(_value); + } - void ASTSymbol::format(stream& io) const { - write(io, symbol_for(_value)); - } + void ASTSymbol::format(stream& io) const { + write(io, symbol_for(_value)); + } - ASTString::ASTString(SourceLocation loc, const string& value): - ASTNode(loc), _value(value) {} + ASTString::ASTString(SourceLocation loc, const string& value) : ASTNode(loc), _value(value) {} - const Type* ASTString::lazy_type() { - return STRING; - } + const Type* ASTString::lazy_type() { + return STRING; + } - ref ASTString::emit(ref& parent) { - return newref(parent, _value); - } + ref ASTString::emit(ref& parent) { + return newref(parent, _value); + } - Location ASTString::emit(Function& func) { - return func.add(new AddressInsn(const_loc(next_label(), _value), type())); - } + Location ASTString::emit(Function& func) { + return func.add(new AddressInsn(const_loc(next_label(), _value), type())); + } - void ASTString::format(stream& io) const { - write(io, _value); - } + void ASTString::format(stream& io) const { + write(io, _value); + } - ASTBool::ASTBool(SourceLocation loc, bool value): - ASTNode(loc), _value(value) {} - - const Type* ASTBool::lazy_type() { - return BOOL; - } - - ref ASTBool::emit(ref& parent) { - return newref(parent, _value); - } - - Location ASTBool::emit(Function& func) { - return loc_immediate(_value ? 1 : 0); - } - - void ASTBool::format(stream& io) const { - write(io, _value); - } - - ASTVar::ASTVar(SourceLocation loc, const ref env, u64 name): - ASTNode(loc), _env(env), _name(name) {} - - const Type* ASTVar::lazy_type() { - const Def* def = _env->find(symbol_for(_name)); - if (def && def->value.is_runtime()) - return ((const RuntimeType*)def->value.type())->base(); - - err(loc(), "Undefined variable '", symbol_for(_name), "'."); - return ERROR; - } - - ref ASTVar::emit(ref& parent) { - return nullptr; // todo - } - - Location ASTVar::emit(Function& func) { - const Def* def = _env->find(symbol_for(_name)); - if (!def) return loc_none(); - return def->location; - // return func.add(new LoadInsn(def->location)); <-- why? - } - - void ASTVar::format(stream& io) const { - write(io, symbol_for(_name)); - } - - ASTExtern::ASTExtern(SourceLocation loc, const Type* type): - ASTNode(loc), _type(type) {} - - const Type* ASTExtern::lazy_type() { - return _type; - } - - ref ASTExtern::emit(ref& parent) { - return nullptr; // todo - } - - Location ASTExtern::emit(Function& function) { - return loc_none(); - } - - void ASTExtern::format(stream& io) const { - write(io, "extern"); - } - - bool ASTExtern::is_extern() const { - return true; - } - - ASTUnary::ASTUnary(SourceLocation loc, ASTNode* child): - ASTNode(loc), _child(child) { - _child->inc(); - } - - ASTUnary::~ASTUnary() { - _child->dec(); - } - - ASTBinary::ASTBinary(SourceLocation loc, ASTNode* left, ASTNode* right): ASTNode(loc), _left(left), _right(right) { - _left->inc(), _right->inc(); - } - - ASTBinary::~ASTBinary() { - _left->dec(), _right->dec(); - } - - ASTBinaryMath::ASTBinaryMath(SourceLocation loc, ASTMathOp op, - ASTNode* left, ASTNode* right): - ASTBinary(loc, left, right), _op(op) {} - - const Type* ASTBinaryMath::lazy_type() { - if (_left->type() == ERROR || _right->type() == ERROR) return ERROR; - const Type* lt = unify(_left->type(), INT); - const Type* rt = unify(_right->type(), INT); - const Type* result = unify(lt, rt); - - if (result != INT) { - err(loc(), "Invalid parameters to arithmetic expression: '", - _left->type(), "' and '", _right->type(), "'."); - return ERROR; - } - return result; - } - - ref ASTBinaryMath::emit(ref& parent) { - switch (_op) { - case AST_ADD: - return newref(parent, type(), SSA_ADD, _left->emit(parent), _right->emit(parent)); - case AST_SUB: - return newref(parent, type(), SSA_SUB, _left->emit(parent), _right->emit(parent)); - case AST_MUL: - return newref(parent, type(), SSA_MUL, _left->emit(parent), _right->emit(parent)); - case AST_DIV: - return newref(parent, type(), SSA_DIV, _left->emit(parent), _right->emit(parent)); - case AST_REM: - return newref(parent, type(), SSA_REM, _left->emit(parent), _right->emit(parent)); - default: - return nullptr; - } - } - - Location ASTBinaryMath::emit(Function& func) { - switch (_op) { - case AST_ADD: - return func.add(new AddInsn(_left->emit(func), _right->emit(func))); - case AST_SUB: - return func.add(new SubInsn(_left->emit(func), _right->emit(func))); - case AST_MUL: - return func.add(new MulInsn(_left->emit(func), _right->emit(func))); - case AST_DIV: - return func.add(new DivInsn(_left->emit(func), _right->emit(func))); - case AST_REM: - return func.add(new RemInsn(_left->emit(func), _right->emit(func))); - default: - return loc_none(); - } - } - - static const char* MATH_OP_NAMES[] = { - "+", "-", "*", "/", "%" - }; - - void ASTBinaryMath::format(stream& io) const { - write(io, "(", MATH_OP_NAMES[_op], " ", _left, " ", _right, ")"); - } - - ASTBinaryLogic::ASTBinaryLogic(SourceLocation loc, ASTLogicOp op, - ASTNode* left, ASTNode* right): - ASTBinary(loc, left, right), _op(op) {} - - const Type* ASTBinaryLogic::lazy_type() { - if (_left->type() == ERROR || _right->type() == ERROR) return ERROR; - const Type* lt = unify(_left->type(), BOOL); - const Type* rt = unify(_right->type(), BOOL); - const Type* result = unify(lt, rt); - - if (result != BOOL) { - err(loc(), "Invalid parameters to logical expression: '", - _left->type(), "' and '", _right->type(), "'."); - return ERROR; - } - return BOOL; - } - - ref ASTBinaryLogic::emit(ref& parent) { - switch (_op) { - case AST_AND: - return newref(parent, type(), SSA_AND, _left->emit(parent), _right->emit(parent)); - case AST_OR: - return newref(parent, type(), SSA_OR, _left->emit(parent), _right->emit(parent)); - case AST_XOR: - return newref(parent, type(), SSA_XOR, _left->emit(parent), _right->emit(parent)); - default: - return nullptr; - } - } - - Location ASTBinaryLogic::emit(Function& func) { - switch (_op) { - case AST_AND: - return func.add(new AndInsn(_left->emit(func), _right->emit(func))); - case AST_OR: - return func.add(new OrInsn(_left->emit(func), _right->emit(func))); - case AST_XOR: - return func.add(new XorInsn(_left->emit(func), _right->emit(func))); - default: - return loc_none(); - } - } - - static const char* LOGIC_OP_NAMES[] = { - "and", "or", "xor", "not" - }; - - void ASTBinaryLogic::format(stream& io) const { - write(io, "(", LOGIC_OP_NAMES[_op], " ", _left, " ", _right, ")"); - } - - ASTNot::ASTNot(SourceLocation loc, ASTNode* child): - ASTUnary(loc, child) {} - - const Type* ASTNot::lazy_type() { - if (_child->type() == ERROR) return ERROR; - if (unify(_child->type(), BOOL) != BOOL) { - err(loc(), "Invalid argument to 'not' expression: '", - _child->type(), "'."); - return ERROR; - } - return BOOL; - } - - ref ASTNot::emit(ref& parent) { - return newref(parent, type(), SSA_NOT, _child->emit(parent)); - } - - Location ASTNot::emit(Function& func) { - return func.add(new NotInsn(_child->emit(func))); - } - - void ASTNot::format(stream& io) const { - write(io, "(not ", _child, ")"); - } - - ASTBinaryEqual::ASTBinaryEqual(SourceLocation loc, ASTEqualOp op, - ASTNode* left, ASTNode* right): - ASTBinary(loc, left, right), _op(op) {} - - const Type* ASTBinaryEqual::lazy_type() { - if (_left->type() == ERROR || _right->type() == ERROR) return ERROR; - return BOOL; - } - - ref ASTBinaryEqual::emit(ref& parent) { - switch (_op) { - case AST_EQUAL: - return newref(parent, type(), SSA_EQ, _left->emit(parent), _right->emit(parent)); - case AST_INEQUAL: - return newref(parent, type(), SSA_NOT_EQ, _left->emit(parent), _right->emit(parent)); - default: - return nullptr; - } - } - - Location ASTBinaryEqual::emit(Function& func) { - if (_left->type() == STRING || _right->type() == STRING) { - vector args; - args.push(_left->emit(func)); - args.push(_right->emit(func)); - Location label; - label.type = LOC_LABEL; - label.label_index = find_label("_strcmp"); - Location result = func.add(new CallInsn(label, args, INT)); - return func.add(new EqualInsn(result, loc_immediate(0))); - } - - switch (_op) { - case AST_EQUAL: - return func.add(new EqualInsn(_left->emit(func), _right->emit(func))); - case AST_INEQUAL: - return func.add(new InequalInsn(_left->emit(func), _right->emit(func))); - default: - return loc_none(); - } - } - - static const char* EQUAL_OP_NAMES[] = { - "==", "!=" - }; - - void ASTBinaryEqual::format(stream& io) const { - write(io, "(", EQUAL_OP_NAMES[_op], " ", _left, " ", _right, ")"); - } - - ASTBinaryRel::ASTBinaryRel(SourceLocation loc, ASTRelOp op, - ASTNode* left, ASTNode* right): - ASTBinary(loc, left, right), _op(op) {} - - const Type* ASTBinaryRel::lazy_type() { - if (_left->type() == ERROR || _right->type() == ERROR) return ERROR; - const Type* lt = unify(_left->type(), INT); - const Type* rt = unify(_right->type(), INT); - const Type* result = unify(lt, rt); - - if (result != INT) { - lt = unify(_left->type(), STRING); - rt = unify(_right->type(), STRING); - result = unify(lt, rt); - if (result != STRING) { - err(loc(), "Invalid parameters to relational expression: '", - _left->type(), "' and '", _right->type(), "'."); - return ERROR; - } - } - return BOOL; - } - - ref ASTBinaryRel::emit(ref& parent) { - switch (_op) { - case AST_LESS: - return newref(parent, type(), SSA_LESS, _left->emit(parent), _right->emit(parent)); - case AST_LESS_EQUAL: - return newref(parent, type(), SSA_LESS_EQ, _left->emit(parent), _right->emit(parent)); - case AST_GREATER: - return newref(parent, type(), SSA_GREATER, _left->emit(parent), _right->emit(parent)); - case AST_GREATER_EQUAL: - return newref(parent, type(), SSA_GREATER_EQ, _left->emit(parent), _right->emit(parent)); - default: - return nullptr; - } - } - - Location ASTBinaryRel::emit(Function& func) { - if (_left->type() == STRING || _right->type() == STRING) { - vector args; - args.push(_left->emit(func)); - args.push(_right->emit(func)); - Location label; - label.type = LOC_LABEL; - label.label_index = find_label("_strcmp"); - Location result = func.add(new CallInsn(label, args, INT)); - - switch(_op) { - case AST_LESS: - return func.add(new LessInsn(result, loc_immediate(0))); - case AST_LESS_EQUAL: - return func.add(new LessEqualInsn(result, loc_immediate(0))); - case AST_GREATER: - return func.add(new GreaterInsn(result, loc_immediate(0))); - case AST_GREATER_EQUAL: - return func.add(new GreaterEqualInsn(result, loc_immediate(0))); - default: - return loc_none(); - } - } - - switch (_op) { - case AST_LESS: - return func.add(new LessInsn(_left->emit(func), _right->emit(func))); - case AST_LESS_EQUAL: - return func.add(new LessEqualInsn(_left->emit(func), _right->emit(func))); - case AST_GREATER: - return func.add(new GreaterInsn(_left->emit(func), _right->emit(func))); - case AST_GREATER_EQUAL: - return func.add(new GreaterEqualInsn(_left->emit(func), _right->emit(func))); - default: - return loc_none(); - } - } - - static const char* REL_OP_NAMES[] = { - "<", "<=", ">", ">=" - }; - - void ASTBinaryRel::format(stream& io) const { - write(io, "(", REL_OP_NAMES[_op], " ", _left, " ", _right, ")"); - } - - ASTDefine::ASTDefine(SourceLocation loc, ref env, u64 name, ASTNode* value): - ASTUnary(loc, value), _env(env), _name(name) {} - - const Type* ASTDefine::lazy_type() { - return VOID; - } - - ref ASTDefine::emit(ref& parent) { - return newref(parent, _env, _name, _child->emit(parent)); - } - - Location ASTDefine::emit(Function& func) { - Location loc = func.create_local(symbol_for(_name), type()); - _env->find(symbol_for(_name))->location = loc; - func.add(new StoreInsn(loc, _child->emit(func))); - return loc_none(); - } - - void ASTDefine::format(stream& io) const { - write(io, "(def ", symbol_for(_name), " ", _child, ")"); - } - - ASTCall::ASTCall(SourceLocation loc, ASTNode* func, const vector& args): - ASTNode(loc), _func(func), _args(args) { - _func->inc(); - for (ASTNode* n : _args) n->inc(); - } - - ASTCall::~ASTCall() { - _func->dec(); - for (ASTNode* n : _args) n->dec(); - } - - const Type* ASTCall::lazy_type() { - const Type* fntype = _func->type(); - const Type* argt = ((const FunctionType*)fntype)->arg(); - if (fntype == ERROR || argt == ERROR) return ERROR; - vector argts; - for (u32 i = 0; i < _args.size(); i ++) { - if (_args[i]->type() == ERROR) return ERROR; - argts.push(_args[i]->type()); - } - const Type* provided_argt = find(argts); - // println("calling ", _func, argt, " on ", provided_argt); - if (!unify(argt, provided_argt)) { - err(loc(), "Invalid arguments ", provided_argt, " to ", _func, "."); - return ERROR; - } - return ((const FunctionType*)fntype)->ret(); - } - - ref ASTCall::emit(ref& parent) { - vector> args; - for (ASTNode* n : _args) args.push(n->emit(parent)); - return newref(parent, type(), _func->emit(parent), args); - } - - Location ASTCall::emit(Function& func) { - Location fn = _func->emit(func); - vector arglocs; - const Type* argt = ((const FunctionType*)_func->type())->arg(); - for (u32 i = 0; i < _args.size(); i ++) { - arglocs.push(_args[i]->emit(func)); - } - for (u32 i = 0; i < _args.size(); i ++) { - if (arglocs[i].type == LOC_LABEL) { - arglocs[i] = func.add(new AddressInsn(arglocs[i], - ((const ProductType*)argt)->member(i))); - } - } - return func.add(new CallInsn(fn, arglocs, type())); - } - - void ASTCall::format(stream& io) const { - write(io, "(", _func); - for (ASTNode* n : _args) write(io, " ", n); - write(io, ")"); - } - - const Type* ASTIncompleteFn::lazy_type() { - return find(_args, find()); - } - - ASTIncompleteFn::ASTIncompleteFn(SourceLocation loc, const Type* args, i64 name): - ASTNode(loc), _args(args), _name(name) {} - - Location ASTIncompleteFn::emit(Function& func) { - return loc_label(symbol_for(_name)); - } - - void ASTIncompleteFn::format(stream& io) const { - if (_name == -1) write(io, ""); - else write(io, symbol_for(_name)); - } - - ASTFunction::ASTFunction(SourceLocation loc, ref env, const Type* args_type, - const vector& args, ASTNode* body, i64 name): - ASTNode(loc), _env(env), _args_type(args_type), _args(args), - _body(body), _name(name), _emitted(false) { - _body->inc(); - } - - ASTFunction::~ASTFunction() { - _body->dec(); - } - - const Type* ASTFunction::lazy_type() { - if (_args_type == ERROR || _body->type() == ERROR) return ERROR; - return find(_args_type, _body->type()); - } - - ref ASTFunction::emit(ref& parent) { - - } - - Location ASTFunction::emit(Function& func) { - if (_body->is_extern()) { - _emitted = true; - _label = add_label(symbol_for(_name)); - Location loc; - loc.type = LOC_LABEL; - loc.label_index = _label; - return func.add(new AddressInsn(loc, type())); - } - else if (!_emitted) { - _emitted = true; - Function& fn = _name == -1 ? func.create_function() - : func.create_function(symbol_for(_name)); - _label = fn.label(); - for (u32 i = 0; i < _args.size(); i ++) { - Def* def = _env->find(symbol_for(_args[i])); - if (def) def->location = fn.add(new LoadArgumentInsn(i, - ((ProductType*)_args_type)->member(i))); - } - fn.add(new RetInsn(_body->emit(fn))); - fn.last()->succ().clear(); - } - - Location loc; - loc.type = LOC_LABEL; - loc.label_index = _label; - return loc; - } - - void ASTFunction::format(stream& io) const { - if (_name == -1) write(io, ""); - else write(io, symbol_for(_name)); - } - - u32 ASTFunction::label() const { - return _label; - } - - ASTBlock::ASTBlock(SourceLocation loc, const vector& exprs): - ASTNode(loc), _exprs(exprs) { - for (ASTNode* n : _exprs) n->inc(); - } - - ASTBlock::~ASTBlock() { - for (ASTNode* n : _exprs) n->dec(); - } - - const Type* ASTBlock::lazy_type() { - for (ASTNode* n : _exprs) if (n->type() == ERROR) return ERROR; - return _exprs.back()->type(); - } - - Location ASTBlock::emit(Function& func) { - Location loc; - for (ASTNode* n : _exprs) loc = n->emit(func); - return loc; - } - - void ASTBlock::format(stream& io) const { - write(io, "(do"); - for (ASTNode* n : _exprs) write(io, " ", n); - write(io, ")"); - } - - ASTIf::ASTIf(SourceLocation loc, ASTNode* cond, ASTNode* if_true, ASTNode* if_false): - ASTNode(loc), _cond(cond), _if_true(if_true), _if_false(if_false) { - _cond->inc(); - _if_true->inc(); - _if_false->inc(); - } - - ASTIf::~ASTIf() { - _cond->dec(); - _if_true->dec(); - _if_false->dec(); - } - - const Type* ASTIf::lazy_type() { - if (_cond->type() == ERROR || _if_true->type() == ERROR - || _if_false->type() == ERROR) return ERROR; - if (unify(_cond->type(), BOOL) != BOOL) { - err(_cond->loc(), "Expected condition of type 'bool', given '", - _cond->type(), "'."); - return ERROR; - } - const Type* left = _if_true->type(), *right = _if_false->type(); - const Type* t = unify(left, right); - if (!t) { - err(loc(), "Could not unify types for branches of if expression: '", - left, "' and '", right, "'."); - return ERROR; - } - - return t; - } - - Location ASTIf::emit(Function& func) { - u32 _else = next_label(), _end = next_label(); - Location result = func.create_local(type()); - func.add(new IfZeroInsn(_else, _cond->emit(func))); - Insn* ifz = func.last(); - Location true_result = _if_true->emit(func); - func.add(new StoreInsn(result, true_result)); - func.add(new GotoInsn(_end)); - Insn* skip = func.last(); - func.add(new Label(_else)); - Insn* elselbl = func.last(); - Location false_result = _if_false->emit(func); - func.add(new StoreInsn(result, false_result)); - func.add(new Label(_end)); - Insn* end = func.last(); - ifz->succ().push(elselbl); // ifz -> else - skip->succ()[0] = end; // goto -> end - return result; - } - - void ASTIf::format(stream& io) const { - write(io, "(if ", _cond, " ", _if_true, " ", _if_false, ")"); - } - - ASTWhile::ASTWhile(SourceLocation loc, ASTNode* cond, ASTNode* body): - ASTNode(loc), _cond(cond), _body(body) { - _cond->inc(); - _body->inc(); - } - - ASTWhile::~ASTWhile() { - _cond->dec(); - _body->dec(); - } - - const Type* ASTWhile::lazy_type() { - const Type* t = unify(_cond->type(), BOOL); - if (!t) { - err(loc(), "Invalid condition in 'while' statement: '", - _cond->type(), "'."); - return ERROR; - } - if (_body->type() == ERROR) return ERROR; - return VOID; - } - - Location ASTWhile::emit(Function& func) { - u32 _start = next_label(), _end = next_label(); - Location result = func.create_local(type()); - func.add(new Label(_start)); - Insn* start = func.last(); - func.add(new IfZeroInsn(_end, _cond->emit(func))); - Insn* ifz = func.last(); - _body->emit(func); - func.add(new GotoInsn(_start)); - Insn* loop = func.last(); - func.add(new Label(_end)); - Insn* end = func.last(); - ifz->succ().push(end); - loop->succ()[0] = start; - return result; - } - - void ASTWhile::format(stream& io) const { - write(io, "(while ", _cond, " ", _body, ")"); - } - - ASTIsEmpty::ASTIsEmpty(SourceLocation loc, ASTNode* list): - ASTUnary(loc, list) {} - - const Type* ASTIsEmpty::lazy_type() { - if (_child->type() == ERROR) return ERROR; - const Type* ct = _child->type(); - if (ct->kind() != KIND_LIST && !ct->concrete()) - ct = unify(ct, find(find())); - if (!ct || (ct->kind() != KIND_LIST && ct != VOID)) { - err(_child->loc(), "Invalid argument to 'empty?' expression: '", - _child->type(), "'."); - return ERROR; - } - return BOOL; - } - - Location ASTIsEmpty::emit(Function& func) { - return func.add(new EqualInsn(_child->emit(func), loc_immediate(0))); - } - - void ASTIsEmpty::format(stream& io) const { - write(io, "(empty? ", _child, ")"); - } - - ASTHead::ASTHead(SourceLocation loc, ASTNode* list): - ASTUnary(loc, list) {} - - const Type* ASTHead::lazy_type() { - if (_child->type() == ERROR) return ERROR; - const Type* ct = _child->type(); - if ((ct->kind() != KIND_LIST && !ct->concrete()) || ct == VOID) - ct = unify(ct, find(find())); - if (!ct || ct->kind() != KIND_LIST) { - err(_child->loc(), "Invalid argument to 'head' expression: '", - _child->type(), "'."); - return ERROR; - } - return ((const ListType*) _child->type())->element(); - } - - Location ASTHead::emit(Function& func) { - return func.add(new LoadPtrInsn(_child->emit(func), type(), 0)); - } - - void ASTHead::format(stream& io) const { - write(io, "(head ", _child, ")"); - } - - ASTTail::ASTTail(SourceLocation loc, ASTNode* list): - ASTUnary(loc, list) {} - - const Type* ASTTail::lazy_type() { - if (_child->type() == ERROR) return ERROR; - const Type* ct = _child->type(); - if ((ct->kind() != KIND_LIST && !ct->concrete()) || ct == VOID) - ct = unify(ct, find(find())); - if (!ct || ct->kind() != KIND_LIST) { - err(_child->loc(), "Invalid argument to 'tail' expression: '", - _child->type(), "'."); - return ERROR; - } - return _child->type(); - } - - Location ASTTail::emit(Function& func) { - return func.add(new LoadPtrInsn(_child->emit(func), type(), 8)); - } - - void ASTTail::format(stream& io) const { - write(io, "(tail ", _child, ")"); - } - - ASTCons::ASTCons(SourceLocation loc, ASTNode* first, ASTNode* rest): - ASTBinary(loc, first, rest) {} - - const Type* ASTCons::lazy_type() { - const Type *first = _left->type(), *rest = _right->type(); - if (first == ERROR || rest == ERROR) return ERROR; - if (rest == VOID) return find(first); - else { - if (rest->kind() == KIND_TYPEVAR) - return unify(rest, find(first)); - if (rest->kind() != KIND_LIST) { - err(_right->loc(), "Invalid argument to 'cons' expression."); - return ERROR; - } - const Type* element = ((const ListType*)rest)->element(); - if (unify(first, element) != element) { - err(_left->loc(), "Invalid arguments to 'cons' expression: '", - first, "' and '", rest, "'."); - return ERROR; - } - return rest; - } - } - - Location ASTCons::emit(Function& func) { - vector args; - args.push(_left->emit(func)); - args.push(_right->emit(func)); - Location label; - label.type = LOC_LABEL; - label.label_index = find_label("_cons"); - return func.add(new CallInsn(label, args, type())); - } - - void ASTCons::format(stream& io) const { - write(io, "(cons ", _left, " ", _right, ")"); - } - - const Type* ASTLength::lazy_type() { - const Type *child = _child->type(); - if (child == ERROR) return ERROR; - if (unify(_child->type(), STRING) != STRING - && unify(_child->type(), find(find()))->kind() - != KIND_LIST) { - err(_child->loc(), "Argument to 'length' expression must be string or list, ", - "given '", _child->type()); - return ERROR; - } - return INT; - } - - ASTLength::ASTLength(SourceLocation loc, ASTNode* child): - basil::ASTUnary(loc, child) {} - - Location ASTLength::emit(Function& func) { - vector args; - args.push(_child->emit(func)); - - Location label; - label.type = LOC_LABEL; - if (_child->type() == STRING) label.label_index = find_label("_strlen"); - else label.label_index = find_label("_listlen"); - - return func.add(new CallInsn(label, args, INT)); - } - - void ASTLength::format(stream& io) const { - write(io, "(length ", _child, ")"); - } - - const Type* ASTDisplay::lazy_type() { - return VOID; - } - - ASTDisplay::ASTDisplay(SourceLocation loc, ASTNode* node): - ASTUnary(loc, node) {} - - Location ASTDisplay::emit(Function& func) { - const char* name; - if (_child->type() == INT) name = "_display_int"; - else if (_child->type() == SYMBOL) name = "_display_symbol"; - else if (_child->type() == BOOL) name = "_display_bool"; - else if (_child->type() == STRING) name = "_display_string"; - else if (_child->type() == find(INT)) name = "_display_int_list"; - else if (_child->type() == find(SYMBOL)) name = "_display_symbol_list"; - else if (_child->type() == find(BOOL)) name = "_display_bool_list"; - else if (_child->type() == find(STRING)) name = "_display_string_list"; - else if (_child->type() == VOID) name = "_display_int_list"; - vector args; - args.push(_child->emit(func)); - Location label; - label.type = LOC_LABEL; - label.label_index = find_label(name); - return func.add(new CallInsn(label, args, type())); - } - - void ASTDisplay::format(stream& io) const { - write(io, "(display ", _child, ")"); - } - - ASTNativeCall::ASTNativeCall(SourceLocation loc, const string &func_name, const Type* ret): - basil::ASTNode(loc), _func_name(func_name), _ret(ret) {} - - ASTNativeCall::ASTNativeCall(SourceLocation loc, const string &func_name, const Type *ret, const vector& args, const vector& arg_types): - ASTNode(loc), _func_name(func_name), _ret(ret), _args(args), _arg_types(arg_types) { - for (ASTNode* node : _args) node->inc(); - } - - ASTNativeCall::~ASTNativeCall() { - for (ASTNode* node : _args) node->dec(); - } - - const Type* ASTNativeCall::lazy_type() { - for (int i = 0; i < _args.size(); i ++) { - if (!unify(_args[i]->type(), _arg_types[i])) { - err(_args[i]->loc(), "Expected '", _arg_types[i], "', given '", _args[i]->type(), "'."); - } - } - return _ret; - } - - Location ASTNativeCall::emit(Function& func) { - vector args; - for (int i = 0; i < _args.size(); i ++) - args.push(_args[i]->emit(func)); - Location label; - label.type = LOC_LABEL; - label.label_index = find_label(_func_name); - return func.add(new CallInsn(label, args, _ret)); - } - - void ASTNativeCall::format(stream& io) const { - write(io, "(", _func_name); - for (const ASTNode *arg : _args) write(io, " ", arg); - write(io, ")"); - } - - ASTAssign::ASTAssign(SourceLocation loc, const ref env, - u64 dest, ASTNode* src): ASTUnary(loc, src), _env(env), _dest(dest) {} - - const Type* ASTAssign::lazy_type() { - const Type* src_type = _child->type(); - const Def* def = _env->find(symbol_for(_dest)); - const Type* dest_type = ((const RuntimeType*)def->value.type())->base(); - if (src_type == ERROR || dest_type == ERROR) - return ERROR; - if (!unify(src_type, dest_type)) { - err(loc(), "Invalid arguments to assignment '", src_type, "' and '", dest_type, "'."); - return ERROR; - } - return VOID; - } - - Location ASTAssign::emit(Function& func) { - const Def* def = _env->find(symbol_for(_dest)); - if (!def) return loc_none(); - return func.add(new StoreInsn(def->location, _child->emit(func))); - } - - void ASTAssign::format(stream& io) const { - write(io, "(= ", symbol_for(_dest), " ", _child, ")"); - } - - ASTAnnotate::ASTAnnotate(SourceLocation loc, ASTNode* value, const Type* type): - ASTNode(loc), _value(value), _type(type) { - _value->inc(); - } - - ASTAnnotate::~ASTAnnotate() { - _value->dec(); - } - - const Type* ASTAnnotate::lazy_type() { - const Type* inferred = unify(_value->type(), _type); - if (!inferred) { - err(_value->loc(), "Could not assign type '", _type, - "' to value of incompatible type '", _value->type(), "'."); - return ERROR; - } - return inferred; - } - - ref ASTAnnotate::emit(ref& parent) { - return _value->emit(parent); - } - - Location ASTAnnotate::emit(Function& function) { - return _value->emit(function); - } - - void ASTAnnotate::format(stream& io) const { - write(io, "(: ", _value, " ", _type, ")"); - } -} + ASTBool::ASTBool(SourceLocation loc, bool value) : ASTNode(loc), _value(value) {} + + const Type* ASTBool::lazy_type() { + return BOOL; + } + + ref ASTBool::emit(ref& parent) { + return newref(parent, _value); + } + + Location ASTBool::emit(Function& func) { + return loc_immediate(_value ? 1 : 0); + } + + void ASTBool::format(stream& io) const { + write(io, _value); + } + + ASTVar::ASTVar(SourceLocation loc, const ref env, u64 name) : ASTNode(loc), _env(env), _name(name) {} + + const Type* ASTVar::lazy_type() { + const Def* def = _env->find(symbol_for(_name)); + if (def && def->value.is_runtime()) return ((const RuntimeType*)def->value.type())->base(); + + err(loc(), "Undefined variable '", symbol_for(_name), "'."); + return ERROR; + } + + ref ASTVar::emit(ref& parent) { + return nullptr; // todo + } + + Location ASTVar::emit(Function& func) { + const Def* def = _env->find(symbol_for(_name)); + if (!def) return loc_none(); + return def->location; + // return func.add(new LoadInsn(def->location)); <-- why? + } + + void ASTVar::format(stream& io) const { + write(io, symbol_for(_name)); + } + + ASTExtern::ASTExtern(SourceLocation loc, const Type* type) : ASTNode(loc), _type(type) {} + + const Type* ASTExtern::lazy_type() { + return _type; + } + + ref ASTExtern::emit(ref& parent) { + return nullptr; // todo + } + + Location ASTExtern::emit(Function& function) { + return loc_none(); + } + + void ASTExtern::format(stream& io) const { + write(io, "extern"); + } + + bool ASTExtern::is_extern() const { + return true; + } + + ASTUnary::ASTUnary(SourceLocation loc, ASTNode* child) : ASTNode(loc), _child(child) { + _child->inc(); + } + + ASTUnary::~ASTUnary() { + _child->dec(); + } + + ASTBinary::ASTBinary(SourceLocation loc, ASTNode* left, ASTNode* right) : ASTNode(loc), _left(left), _right(right) { + _left->inc(), _right->inc(); + } + + ASTBinary::~ASTBinary() { + _left->dec(), _right->dec(); + } + + ASTBinaryMath::ASTBinaryMath(SourceLocation loc, ASTMathOp op, ASTNode* left, ASTNode* right) + : ASTBinary(loc, left, right), _op(op) {} + + const Type* ASTBinaryMath::lazy_type() { + if (_left->type() == ERROR || _right->type() == ERROR) return ERROR; + const Type* lt = unify(_left->type(), INT); + const Type* rt = unify(_right->type(), INT); + const Type* result = unify(lt, rt); + + if (result != INT) { + err(loc(), "Invalid parameters to arithmetic expression: '", _left->type(), "' and '", _right->type(), + "'."); + return ERROR; + } + return result; + } + + ref ASTBinaryMath::emit(ref& parent) { + switch (_op) { + case AST_ADD: return newref(parent, type(), SSA_ADD, _left->emit(parent), _right->emit(parent)); + case AST_SUB: return newref(parent, type(), SSA_SUB, _left->emit(parent), _right->emit(parent)); + case AST_MUL: return newref(parent, type(), SSA_MUL, _left->emit(parent), _right->emit(parent)); + case AST_DIV: return newref(parent, type(), SSA_DIV, _left->emit(parent), _right->emit(parent)); + case AST_REM: return newref(parent, type(), SSA_REM, _left->emit(parent), _right->emit(parent)); + default: return nullptr; + } + } + + Location ASTBinaryMath::emit(Function& func) { + switch (_op) { + case AST_ADD: return func.add(new AddInsn(_left->emit(func), _right->emit(func))); + case AST_SUB: return func.add(new SubInsn(_left->emit(func), _right->emit(func))); + case AST_MUL: return func.add(new MulInsn(_left->emit(func), _right->emit(func))); + case AST_DIV: return func.add(new DivInsn(_left->emit(func), _right->emit(func))); + case AST_REM: return func.add(new RemInsn(_left->emit(func), _right->emit(func))); + default: return loc_none(); + } + } + + static const char* MATH_OP_NAMES[] = {"+", "-", "*", "/", "%"}; + + void ASTBinaryMath::format(stream& io) const { + write(io, "(", MATH_OP_NAMES[_op], " ", _left, " ", _right, ")"); + } + + ASTBinaryLogic::ASTBinaryLogic(SourceLocation loc, ASTLogicOp op, ASTNode* left, ASTNode* right) + : ASTBinary(loc, left, right), _op(op) {} + + const Type* ASTBinaryLogic::lazy_type() { + if (_left->type() == ERROR || _right->type() == ERROR) return ERROR; + const Type* lt = unify(_left->type(), BOOL); + const Type* rt = unify(_right->type(), BOOL); + const Type* result = unify(lt, rt); + + if (result != BOOL) { + err(loc(), "Invalid parameters to logical expression: '", _left->type(), "' and '", _right->type(), "'."); + return ERROR; + } + return BOOL; + } + + ref ASTBinaryLogic::emit(ref& parent) { + switch (_op) { + case AST_AND: return newref(parent, type(), SSA_AND, _left->emit(parent), _right->emit(parent)); + case AST_OR: return newref(parent, type(), SSA_OR, _left->emit(parent), _right->emit(parent)); + case AST_XOR: return newref(parent, type(), SSA_XOR, _left->emit(parent), _right->emit(parent)); + default: return nullptr; + } + } + + Location ASTBinaryLogic::emit(Function& func) { + switch (_op) { + case AST_AND: return func.add(new AndInsn(_left->emit(func), _right->emit(func))); + case AST_OR: return func.add(new OrInsn(_left->emit(func), _right->emit(func))); + case AST_XOR: return func.add(new XorInsn(_left->emit(func), _right->emit(func))); + default: return loc_none(); + } + } + + static const char* LOGIC_OP_NAMES[] = {"and", "or", "xor", "not"}; + + void ASTBinaryLogic::format(stream& io) const { + write(io, "(", LOGIC_OP_NAMES[_op], " ", _left, " ", _right, ")"); + } + + ASTNot::ASTNot(SourceLocation loc, ASTNode* child) : ASTUnary(loc, child) {} + + const Type* ASTNot::lazy_type() { + if (_child->type() == ERROR) return ERROR; + if (unify(_child->type(), BOOL) != BOOL) { + err(loc(), "Invalid argument to 'not' expression: '", _child->type(), "'."); + return ERROR; + } + return BOOL; + } + + ref ASTNot::emit(ref& parent) { + return newref(parent, type(), SSA_NOT, _child->emit(parent)); + } + + Location ASTNot::emit(Function& func) { + return func.add(new NotInsn(_child->emit(func))); + } + + void ASTNot::format(stream& io) const { + write(io, "(not ", _child, ")"); + } + + ASTBinaryEqual::ASTBinaryEqual(SourceLocation loc, ASTEqualOp op, ASTNode* left, ASTNode* right) + : ASTBinary(loc, left, right), _op(op) {} + + const Type* ASTBinaryEqual::lazy_type() { + if (_left->type() == ERROR || _right->type() == ERROR) return ERROR; + return BOOL; + } + + ref ASTBinaryEqual::emit(ref& parent) { + switch (_op) { + case AST_EQUAL: return newref(parent, type(), SSA_EQ, _left->emit(parent), _right->emit(parent)); + case AST_INEQUAL: + return newref(parent, type(), SSA_NOT_EQ, _left->emit(parent), _right->emit(parent)); + default: return nullptr; + } + } + + Location ASTBinaryEqual::emit(Function& func) { + if (_left->type() == STRING || _right->type() == STRING) { + vector args; + args.push(_left->emit(func)); + args.push(_right->emit(func)); + Location label; + label.type = LOC_LABEL; + label.label_index = find_label("_strcmp"); + Location result = func.add(new CallInsn(label, args, INT)); + return func.add(new EqualInsn(result, loc_immediate(0))); + } + + switch (_op) { + case AST_EQUAL: return func.add(new EqualInsn(_left->emit(func), _right->emit(func))); + case AST_INEQUAL: return func.add(new InequalInsn(_left->emit(func), _right->emit(func))); + default: return loc_none(); + } + } + + static const char* EQUAL_OP_NAMES[] = {"==", "!="}; + + void ASTBinaryEqual::format(stream& io) const { + write(io, "(", EQUAL_OP_NAMES[_op], " ", _left, " ", _right, ")"); + } + + ASTBinaryRel::ASTBinaryRel(SourceLocation loc, ASTRelOp op, ASTNode* left, ASTNode* right) + : ASTBinary(loc, left, right), _op(op) {} + + const Type* ASTBinaryRel::lazy_type() { + if (_left->type() == ERROR || _right->type() == ERROR) return ERROR; + const Type* lt = unify(_left->type(), INT); + const Type* rt = unify(_right->type(), INT); + const Type* result = unify(lt, rt); + + if (result != INT) { + lt = unify(_left->type(), STRING); + rt = unify(_right->type(), STRING); + result = unify(lt, rt); + if (result != STRING) { + err(loc(), "Invalid parameters to relational expression: '", _left->type(), "' and '", _right->type(), + "'."); + return ERROR; + } + } + return BOOL; + } + + ref ASTBinaryRel::emit(ref& parent) { + switch (_op) { + case AST_LESS: + return newref(parent, type(), SSA_LESS, _left->emit(parent), _right->emit(parent)); + case AST_LESS_EQUAL: + return newref(parent, type(), SSA_LESS_EQ, _left->emit(parent), _right->emit(parent)); + case AST_GREATER: + return newref(parent, type(), SSA_GREATER, _left->emit(parent), _right->emit(parent)); + case AST_GREATER_EQUAL: + return newref(parent, type(), SSA_GREATER_EQ, _left->emit(parent), _right->emit(parent)); + default: return nullptr; + } + } + + Location ASTBinaryRel::emit(Function& func) { + if (_left->type() == STRING || _right->type() == STRING) { + vector args; + args.push(_left->emit(func)); + args.push(_right->emit(func)); + Location label; + label.type = LOC_LABEL; + label.label_index = find_label("_strcmp"); + Location result = func.add(new CallInsn(label, args, INT)); + + switch (_op) { + case AST_LESS: return func.add(new LessInsn(result, loc_immediate(0))); + case AST_LESS_EQUAL: return func.add(new LessEqualInsn(result, loc_immediate(0))); + case AST_GREATER: return func.add(new GreaterInsn(result, loc_immediate(0))); + case AST_GREATER_EQUAL: return func.add(new GreaterEqualInsn(result, loc_immediate(0))); + default: return loc_none(); + } + } + + switch (_op) { + case AST_LESS: return func.add(new LessInsn(_left->emit(func), _right->emit(func))); + case AST_LESS_EQUAL: return func.add(new LessEqualInsn(_left->emit(func), _right->emit(func))); + case AST_GREATER: return func.add(new GreaterInsn(_left->emit(func), _right->emit(func))); + case AST_GREATER_EQUAL: return func.add(new GreaterEqualInsn(_left->emit(func), _right->emit(func))); + default: return loc_none(); + } + } + + static const char* REL_OP_NAMES[] = {"<", "<=", ">", ">="}; + + void ASTBinaryRel::format(stream& io) const { + write(io, "(", REL_OP_NAMES[_op], " ", _left, " ", _right, ")"); + } + + ASTDefine::ASTDefine(SourceLocation loc, ref env, u64 name, ASTNode* value) + : ASTUnary(loc, value), _env(env), _name(name) {} + + const Type* ASTDefine::lazy_type() { + return VOID; + } + + ref ASTDefine::emit(ref& parent) { + return newref(parent, _env, _name, _child->emit(parent)); + } + + Location ASTDefine::emit(Function& func) { + Location loc = func.create_local(symbol_for(_name), type()); + _env->find(symbol_for(_name))->location = loc; + func.add(new StoreInsn(loc, _child->emit(func))); + return loc_none(); + } + + void ASTDefine::format(stream& io) const { + write(io, "(def ", symbol_for(_name), " ", _child, ")"); + } + + ASTCall::ASTCall(SourceLocation loc, ASTNode* func, const vector& args) + : ASTNode(loc), _func(func), _args(args) { + _func->inc(); + for (ASTNode* n : _args) n->inc(); + } + + ASTCall::~ASTCall() { + _func->dec(); + for (ASTNode* n : _args) n->dec(); + } + + const Type* ASTCall::lazy_type() { + const Type* fntype = _func->type(); + const Type* argt = ((const FunctionType*)fntype)->arg(); + if (fntype == ERROR || argt == ERROR) return ERROR; + vector argts; + for (u32 i = 0; i < _args.size(); i++) { + if (_args[i]->type() == ERROR) return ERROR; + argts.push(_args[i]->type()); + } + const Type* provided_argt = find(argts); + // println("calling ", _func, argt, " on ", provided_argt); + if (!unify(argt, provided_argt)) { + err(loc(), "Invalid arguments ", provided_argt, " to ", _func, "."); + return ERROR; + } + return ((const FunctionType*)fntype)->ret(); + } + + ref ASTCall::emit(ref& parent) { + vector> args; + for (ASTNode* n : _args) args.push(n->emit(parent)); + return newref(parent, type(), _func->emit(parent), args); + } + + Location ASTCall::emit(Function& func) { + Location fn = _func->emit(func); + vector arglocs; + const Type* argt = ((const FunctionType*)_func->type())->arg(); + for (u32 i = 0; i < _args.size(); i++) { arglocs.push(_args[i]->emit(func)); } + for (u32 i = 0; i < _args.size(); i++) { + if (arglocs[i].type == LOC_LABEL) { + arglocs[i] = func.add(new AddressInsn(arglocs[i], ((const ProductType*)argt)->member(i))); + } + } + return func.add(new CallInsn(fn, arglocs, type())); + } + + void ASTCall::format(stream& io) const { + write(io, "(", _func); + for (ASTNode* n : _args) write(io, " ", n); + write(io, ")"); + } + + const Type* ASTIncompleteFn::lazy_type() { + return find(_args, find()); + } + + ASTIncompleteFn::ASTIncompleteFn(SourceLocation loc, const Type* args, i64 name) + : ASTNode(loc), _args(args), _name(name) {} + + Location ASTIncompleteFn::emit(Function& func) { + return loc_label(symbol_for(_name)); + } + + void ASTIncompleteFn::format(stream& io) const { + if (_name == -1) write(io, ""); + else + write(io, symbol_for(_name)); + } + + ASTFunction::ASTFunction(SourceLocation loc, ref env, const Type* args_type, const vector& args, + ASTNode* body, i64 name) + : ASTNode(loc), _env(env), _args_type(args_type), _args(args), _body(body), _name(name), _emitted(false) { + _body->inc(); + } + + ASTFunction::~ASTFunction() { + _body->dec(); + } + + const Type* ASTFunction::lazy_type() { + if (_args_type == ERROR || _body->type() == ERROR) return ERROR; + return find(_args_type, _body->type()); + } + + ref ASTFunction::emit(ref& parent) {} + + Location ASTFunction::emit(Function& func) { + if (_body->is_extern()) { + _emitted = true; + _label = add_label(symbol_for(_name)); + Location loc; + loc.type = LOC_LABEL; + loc.label_index = _label; + return func.add(new AddressInsn(loc, type())); + } else if (!_emitted) { + _emitted = true; + Function& fn = _name == -1 ? func.create_function() : func.create_function(symbol_for(_name)); + _label = fn.label(); + for (u32 i = 0; i < _args.size(); i++) { + Def* def = _env->find(symbol_for(_args[i])); + if (def) def->location = fn.add(new LoadArgumentInsn(i, ((ProductType*)_args_type)->member(i))); + } + fn.add(new RetInsn(_body->emit(fn))); + fn.last()->succ().clear(); + } + + Location loc; + loc.type = LOC_LABEL; + loc.label_index = _label; + return loc; + } + + void ASTFunction::format(stream& io) const { + if (_name == -1) write(io, ""); + else + write(io, symbol_for(_name)); + } + + u32 ASTFunction::label() const { + return _label; + } + + ASTBlock::ASTBlock(SourceLocation loc, const vector& exprs) : ASTNode(loc), _exprs(exprs) { + for (ASTNode* n : _exprs) n->inc(); + } + + ASTBlock::~ASTBlock() { + for (ASTNode* n : _exprs) n->dec(); + } + + const Type* ASTBlock::lazy_type() { + for (ASTNode* n : _exprs) + if (n->type() == ERROR) return ERROR; + return _exprs.back()->type(); + } + + Location ASTBlock::emit(Function& func) { + Location loc; + for (ASTNode* n : _exprs) loc = n->emit(func); + return loc; + } + + void ASTBlock::format(stream& io) const { + write(io, "(do"); + for (ASTNode* n : _exprs) write(io, " ", n); + write(io, ")"); + } + + ASTIf::ASTIf(SourceLocation loc, ASTNode* cond, ASTNode* if_true, ASTNode* if_false) + : ASTNode(loc), _cond(cond), _if_true(if_true), _if_false(if_false) { + _cond->inc(); + _if_true->inc(); + _if_false->inc(); + } + + ASTIf::~ASTIf() { + _cond->dec(); + _if_true->dec(); + _if_false->dec(); + } + + const Type* ASTIf::lazy_type() { + if (_cond->type() == ERROR || _if_true->type() == ERROR || _if_false->type() == ERROR) return ERROR; + if (unify(_cond->type(), BOOL) != BOOL) { + err(_cond->loc(), "Expected condition of type 'bool', given '", _cond->type(), "'."); + return ERROR; + } + const Type *left = _if_true->type(), *right = _if_false->type(); + const Type* t = unify(left, right); + if (!t) { + err(loc(), "Could not unify types for branches of if expression: '", left, "' and '", right, "'."); + return ERROR; + } + + return t; + } + + Location ASTIf::emit(Function& func) { + u32 _else = next_label(), _end = next_label(); + Location result = func.create_local(type()); + func.add(new IfZeroInsn(_else, _cond->emit(func))); + Insn* ifz = func.last(); + Location true_result = _if_true->emit(func); + func.add(new StoreInsn(result, true_result)); + func.add(new GotoInsn(_end)); + Insn* skip = func.last(); + func.add(new Label(_else)); + Insn* elselbl = func.last(); + Location false_result = _if_false->emit(func); + func.add(new StoreInsn(result, false_result)); + func.add(new Label(_end)); + Insn* end = func.last(); + ifz->succ().push(elselbl); // ifz -> else + skip->succ()[0] = end; // goto -> end + return result; + } + + void ASTIf::format(stream& io) const { + write(io, "(if ", _cond, " ", _if_true, " ", _if_false, ")"); + } + + ASTWhile::ASTWhile(SourceLocation loc, ASTNode* cond, ASTNode* body) : ASTNode(loc), _cond(cond), _body(body) { + _cond->inc(); + _body->inc(); + } + + ASTWhile::~ASTWhile() { + _cond->dec(); + _body->dec(); + } + + const Type* ASTWhile::lazy_type() { + const Type* t = unify(_cond->type(), BOOL); + if (!t) { + err(loc(), "Invalid condition in 'while' statement: '", _cond->type(), "'."); + return ERROR; + } + if (_body->type() == ERROR) return ERROR; + return VOID; + } + + Location ASTWhile::emit(Function& func) { + u32 _start = next_label(), _end = next_label(); + Location result = func.create_local(type()); + func.add(new Label(_start)); + Insn* start = func.last(); + func.add(new IfZeroInsn(_end, _cond->emit(func))); + Insn* ifz = func.last(); + _body->emit(func); + func.add(new GotoInsn(_start)); + Insn* loop = func.last(); + func.add(new Label(_end)); + Insn* end = func.last(); + ifz->succ().push(end); + loop->succ()[0] = start; + return result; + } + + void ASTWhile::format(stream& io) const { + write(io, "(while ", _cond, " ", _body, ")"); + } + + ASTIsEmpty::ASTIsEmpty(SourceLocation loc, ASTNode* list) : ASTUnary(loc, list) {} + + const Type* ASTIsEmpty::lazy_type() { + if (_child->type() == ERROR) return ERROR; + const Type* ct = _child->type(); + if (ct->kind() != KIND_LIST && !ct->concrete()) ct = unify(ct, find(find())); + if (!ct || (ct->kind() != KIND_LIST && ct != VOID)) { + err(_child->loc(), "Invalid argument to 'empty?' expression: '", _child->type(), "'."); + return ERROR; + } + return BOOL; + } + + Location ASTIsEmpty::emit(Function& func) { + return func.add(new EqualInsn(_child->emit(func), loc_immediate(0))); + } + + void ASTIsEmpty::format(stream& io) const { + write(io, "(empty? ", _child, ")"); + } + + ASTHead::ASTHead(SourceLocation loc, ASTNode* list) : ASTUnary(loc, list) {} + + const Type* ASTHead::lazy_type() { + if (_child->type() == ERROR) return ERROR; + const Type* ct = _child->type(); + if ((ct->kind() != KIND_LIST && !ct->concrete()) || ct == VOID) + ct = unify(ct, find(find())); + if (!ct || ct->kind() != KIND_LIST) { + err(_child->loc(), "Invalid argument to 'head' expression: '", _child->type(), "'."); + return ERROR; + } + return ((const ListType*)_child->type())->element(); + } + + Location ASTHead::emit(Function& func) { + return func.add(new LoadPtrInsn(_child->emit(func), type(), 0)); + } + + void ASTHead::format(stream& io) const { + write(io, "(head ", _child, ")"); + } + + ASTTail::ASTTail(SourceLocation loc, ASTNode* list) : ASTUnary(loc, list) {} + + const Type* ASTTail::lazy_type() { + if (_child->type() == ERROR) return ERROR; + const Type* ct = _child->type(); + if ((ct->kind() != KIND_LIST && !ct->concrete()) || ct == VOID) + ct = unify(ct, find(find())); + if (!ct || ct->kind() != KIND_LIST) { + err(_child->loc(), "Invalid argument to 'tail' expression: '", _child->type(), "'."); + return ERROR; + } + return _child->type(); + } + + Location ASTTail::emit(Function& func) { + return func.add(new LoadPtrInsn(_child->emit(func), type(), 8)); + } + + void ASTTail::format(stream& io) const { + write(io, "(tail ", _child, ")"); + } + + ASTCons::ASTCons(SourceLocation loc, ASTNode* first, ASTNode* rest) : ASTBinary(loc, first, rest) {} + + const Type* ASTCons::lazy_type() { + const Type *first = _left->type(), *rest = _right->type(); + if (first == ERROR || rest == ERROR) return ERROR; + if (rest == VOID) return find(first); + else { + if (rest->kind() == KIND_TYPEVAR) return unify(rest, find(first)); + if (rest->kind() != KIND_LIST) { + err(_right->loc(), "Invalid argument to 'cons' expression."); + return ERROR; + } + const Type* element = ((const ListType*)rest)->element(); + if (unify(first, element) != element) { + err(_left->loc(), "Invalid arguments to 'cons' expression: '", first, "' and '", rest, "'."); + return ERROR; + } + return rest; + } + } + + Location ASTCons::emit(Function& func) { + vector args; + args.push(_left->emit(func)); + args.push(_right->emit(func)); + Location label; + label.type = LOC_LABEL; + label.label_index = find_label("_cons"); + return func.add(new CallInsn(label, args, type())); + } + + void ASTCons::format(stream& io) const { + write(io, "(cons ", _left, " ", _right, ")"); + } + + const Type* ASTLength::lazy_type() { + const Type* child = _child->type(); + if (child == ERROR) return ERROR; + if (unify(_child->type(), STRING) != STRING && + unify(_child->type(), find(find()))->kind() != KIND_LIST) { + err(_child->loc(), "Argument to 'length' expression must be string or list, ", "given '", _child->type()); + return ERROR; + } + return INT; + } + + ASTLength::ASTLength(SourceLocation loc, ASTNode* child) : basil::ASTUnary(loc, child) {} + + Location ASTLength::emit(Function& func) { + vector args; + args.push(_child->emit(func)); + + Location label; + label.type = LOC_LABEL; + if (_child->type() == STRING) label.label_index = find_label("_strlen"); + else + label.label_index = find_label("_listlen"); + + return func.add(new CallInsn(label, args, INT)); + } + + void ASTLength::format(stream& io) const { + write(io, "(length ", _child, ")"); + } + + const Type* ASTDisplay::lazy_type() { + return VOID; + } + + ASTDisplay::ASTDisplay(SourceLocation loc, ASTNode* node) : ASTUnary(loc, node) {} + + Location ASTDisplay::emit(Function& func) { + const char* name; + if (_child->type() == INT) name = "_display_int"; + else if (_child->type() == SYMBOL) + name = "_display_symbol"; + else if (_child->type() == BOOL) + name = "_display_bool"; + else if (_child->type() == STRING) + name = "_display_string"; + else if (_child->type() == find(INT)) + name = "_display_int_list"; + else if (_child->type() == find(SYMBOL)) + name = "_display_symbol_list"; + else if (_child->type() == find(BOOL)) + name = "_display_bool_list"; + else if (_child->type() == find(STRING)) + name = "_display_string_list"; + else if (_child->type() == VOID) + name = "_display_int_list"; + vector args; + args.push(_child->emit(func)); + Location label; + label.type = LOC_LABEL; + label.label_index = find_label(name); + return func.add(new CallInsn(label, args, type())); + } + + void ASTDisplay::format(stream& io) const { + write(io, "(display ", _child, ")"); + } + + ASTNativeCall::ASTNativeCall(SourceLocation loc, const string& func_name, const Type* ret) + : basil::ASTNode(loc), _func_name(func_name), _ret(ret) {} + + ASTNativeCall::ASTNativeCall(SourceLocation loc, const string& func_name, const Type* ret, + const vector& args, const vector& arg_types) + : ASTNode(loc), _func_name(func_name), _ret(ret), _args(args), _arg_types(arg_types) { + for (ASTNode* node : _args) node->inc(); + } + + ASTNativeCall::~ASTNativeCall() { + for (ASTNode* node : _args) node->dec(); + } + + const Type* ASTNativeCall::lazy_type() { + for (int i = 0; i < _args.size(); i++) { + if (!unify(_args[i]->type(), _arg_types[i])) { + err(_args[i]->loc(), "Expected '", _arg_types[i], "', given '", _args[i]->type(), "'."); + } + } + return _ret; + } + + Location ASTNativeCall::emit(Function& func) { + vector args; + for (int i = 0; i < _args.size(); i++) args.push(_args[i]->emit(func)); + Location label; + label.type = LOC_LABEL; + label.label_index = find_label(_func_name); + return func.add(new CallInsn(label, args, _ret)); + } + + void ASTNativeCall::format(stream& io) const { + write(io, "(", _func_name); + for (const ASTNode* arg : _args) write(io, " ", arg); + write(io, ")"); + } + + ASTAssign::ASTAssign(SourceLocation loc, const ref env, u64 dest, ASTNode* src) + : ASTUnary(loc, src), _env(env), _dest(dest) {} + + const Type* ASTAssign::lazy_type() { + const Type* src_type = _child->type(); + const Def* def = _env->find(symbol_for(_dest)); + const Type* dest_type = ((const RuntimeType*)def->value.type())->base(); + if (src_type == ERROR || dest_type == ERROR) return ERROR; + if (!unify(src_type, dest_type)) { + err(loc(), "Invalid arguments to assignment '", src_type, "' and '", dest_type, "'."); + return ERROR; + } + return VOID; + } + + Location ASTAssign::emit(Function& func) { + const Def* def = _env->find(symbol_for(_dest)); + if (!def) return loc_none(); + return func.add(new StoreInsn(def->location, _child->emit(func))); + } + + void ASTAssign::format(stream& io) const { + write(io, "(= ", symbol_for(_dest), " ", _child, ")"); + } + + ASTAnnotate::ASTAnnotate(SourceLocation loc, ASTNode* value, const Type* type) + : ASTNode(loc), _value(value), _type(type) { + _value->inc(); + } + + ASTAnnotate::~ASTAnnotate() { + _value->dec(); + } + + const Type* ASTAnnotate::lazy_type() { + const Type* inferred = unify(_value->type(), _type); + if (!inferred) { + err(_value->loc(), "Could not assign type '", _type, "' to value of incompatible type '", _value->type(), + "'."); + return ERROR; + } + return inferred; + } + + ref ASTAnnotate::emit(ref& parent) { + return _value->emit(parent); + } + + Location ASTAnnotate::emit(Function& function) { + return _value->emit(function); + } + + void ASTAnnotate::format(stream& io) const { + write(io, "(: ", _value, " ", _type, ")"); + } +} // namespace basil void write(stream& io, basil::ASTNode* n) { - n->format(io); + n->format(io); } void write(stream& io, const basil::ASTNode* n) { - n->format(io); + n->format(io); } \ No newline at end of file diff --git a/compiler/ast.h b/compiler/ast.h index fa910aa..79aade3 100644 --- a/compiler/ast.h +++ b/compiler/ast.h @@ -1,455 +1,486 @@ #ifndef BASIL_AST_H #define BASIL_AST_H -#include "util/defs.h" -#include "util/str.h" -#include "util/rc.h" -#include "util/io.h" #include "errors.h" +#include "ir.h" +#include "ssa.h" #include "type.h" +#include "util/defs.h" +#include "util/io.h" +#include "util/rc.h" +#include "util/str.h" #include "values.h" -#include "ssa.h" -#include "ir.h" namespace basil { - class Def; - class Env; - - class ASTNode : public RC { - SourceLocation _loc; - const Type* _type; - protected: - virtual const Type* lazy_type() = 0; - public: - ASTNode(SourceLocation loc); - virtual ~ASTNode(); - - SourceLocation loc() const; - const Type* type(); - virtual bool is_extern() const; - virtual ref emit(ref& parent) = 0; - virtual Location emit(Function& function) = 0; - virtual void format(stream& io) const = 0; - }; - - class ASTSingleton : public ASTNode { - const Type* _type; - protected: - const Type* lazy_type() override; - public: - ASTSingleton(const Type* type); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTVoid : public ASTNode { - protected: - const Type* lazy_type() override; - public: - ASTVoid(SourceLocation loc); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTInt : public ASTNode { - i64 _value; - protected: - const Type* lazy_type() override; - public: - ASTInt(SourceLocation loc, i64 value); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTSymbol : public ASTNode { - u64 _value; - protected: - const Type* lazy_type() override; - public: - ASTSymbol(SourceLocation loc, u64 value); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTString : public ASTNode { - string _value; - protected: - const Type* lazy_type() override; - public: - ASTString(SourceLocation, const string& value); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTBool : public ASTNode { - bool _value; - protected: - const Type* lazy_type() override; - public: - ASTBool(SourceLocation loc, bool value); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTVar : public ASTNode { - ref _env; - u64 _name; - protected: - const Type* lazy_type() override; - public: - ASTVar(SourceLocation loc, const ref env, u64 name); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTExtern : public ASTNode { - const Type* _type; - protected: - const Type* lazy_type() override; - public: - ASTExtern(SourceLocation loc, const Type* type); - - bool is_extern() const override; - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTUnary : public ASTNode { - protected: - ASTNode *_child; - public: - ASTUnary(SourceLocation loc, ASTNode* child); - ~ASTUnary(); - }; - - class ASTBinary : public ASTNode { - protected: - ASTNode *_left, *_right; - public: - ASTBinary(SourceLocation loc, ASTNode* left, ASTNode* right); - ~ASTBinary(); - }; - - enum ASTMathOp { - AST_ADD, - AST_SUB, - AST_MUL, - AST_DIV, - AST_REM - }; - - class ASTBinaryMath : public ASTBinary { - ASTMathOp _op; - protected: - const Type* lazy_type() override; - public: - ASTBinaryMath(SourceLocation loc, ASTMathOp op, - ASTNode* left, ASTNode* right); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - enum ASTLogicOp { - AST_AND, - AST_OR, - AST_XOR, - AST_NOT - }; - - class ASTBinaryLogic : public ASTBinary { - ASTLogicOp _op; - protected: - const Type* lazy_type() override; - public: - ASTBinaryLogic(SourceLocation loc, ASTLogicOp op, - ASTNode* left, ASTNode* right); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTNot : public ASTUnary { - protected: - const Type* lazy_type() override; - public: - ASTNot(SourceLocation loc, ASTNode* child); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - enum ASTEqualOp { - AST_EQUAL, - AST_INEQUAL - }; - - class ASTBinaryEqual : public ASTBinary { - ASTEqualOp _op; - protected: - const Type* lazy_type() override; - public: - ASTBinaryEqual(SourceLocation loc, ASTEqualOp op, - ASTNode* left, ASTNode* right); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - enum ASTRelOp { - AST_LESS, - AST_LESS_EQUAL, - AST_GREATER, - AST_GREATER_EQUAL - }; - - class ASTBinaryRel : public ASTBinary { - ASTRelOp _op; - protected: - const Type* lazy_type() override; - public: - ASTBinaryRel(SourceLocation loc, ASTRelOp op, - ASTNode* left, ASTNode* right); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTDefine : public ASTUnary { - ref _env; - u64 _name; - protected: - const Type* lazy_type() override; - public: - ASTDefine(SourceLocation loc, ref env, u64 name, ASTNode* value); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTCall : public ASTNode { - ASTNode* _func; - vector _args; - protected: - const Type* lazy_type() override; - public: - ASTCall(SourceLocation loc, ASTNode* func, const vector& args); - ~ASTCall(); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTIncompleteFn : public ASTNode { - const Type* _args; - i64 _name; - protected: - const Type* lazy_type() override; - public: - ASTIncompleteFn(SourceLocation loc, const Type* args, i64 name); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTFunction : public ASTNode { - ref _env; - const Type* _args_type; - vector _args; - ASTNode* _body; - i64 _name; - bool _emitted; - u32 _label; - ref _entry, _exit; - protected: - const Type* lazy_type() override; - public: - ASTFunction(SourceLocation loc, ref env, const Type* args_type, - const vector& args, ASTNode* body, i64 name = -1); - ~ASTFunction(); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - u32 label() const; - }; - - class ASTBlock : public ASTNode { - vector _exprs; - protected: - const Type* lazy_type() override; - public: - ASTBlock(SourceLocation loc, const vector& exprs); - ~ASTBlock(); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTIf : public ASTNode { - ASTNode *_cond, *_if_true, *_if_false; - protected: - const Type* lazy_type() override; - public: - ASTIf(SourceLocation loc, ASTNode* cond, ASTNode* if_true, ASTNode* if_false); - ~ASTIf(); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTWhile : public ASTNode { - ASTNode *_cond, *_body; - protected: - const Type* lazy_type() override; - public: - ASTWhile(SourceLocation loc, ASTNode* cond, ASTNode* body); - ~ASTWhile(); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTIsEmpty : public ASTUnary { - protected: - const Type* lazy_type() override; - public: - ASTIsEmpty(SourceLocation loc, ASTNode* list); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTHead : public ASTUnary { - protected: - const Type* lazy_type() override; - public: - ASTHead(SourceLocation loc, ASTNode* list); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTTail : public ASTUnary { - protected: - const Type* lazy_type() override; - public: - ASTTail(SourceLocation loc, ASTNode* list); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTCons : public ASTBinary { - protected: - const Type* lazy_type() override; - public: - ASTCons(SourceLocation loc, ASTNode* first, ASTNode* rest); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTLength : public ASTUnary { - protected: - const Type* lazy_type() override; - public: - ASTLength(SourceLocation loc, ASTNode* child); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTDisplay : public ASTUnary { - protected: - const Type* lazy_type() override; - public: - ASTDisplay(SourceLocation loc, ASTNode* node); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTNativeCall : public ASTNode { - const string _func_name; - const Type * _ret; - const vector _args; - const vector _arg_types; - protected: - const Type* lazy_type() override; - public: - ASTNativeCall(SourceLocation loc, const string &func_name, const Type* ret); - // TODO make args a variadic template instead of a vector - ASTNativeCall(SourceLocation loc, const string &func_name, const Type *ret, const vector& args, const vector& arg_types); - ~ASTNativeCall(); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTAssign : public ASTUnary { - ref _env; - u64 _dest; - protected: - const Type* lazy_type() override; - public: - ASTAssign(SourceLocation loc, const ref env, - u64 dest, ASTNode* src); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; - - class ASTAnnotate : public ASTNode { - ASTNode* _value; - const Type* _type; - protected: - const Type* lazy_type() override; - public: - ASTAnnotate(SourceLocation loc, ASTNode* value, const Type* type); - ~ASTAnnotate(); - - ref emit(ref& parent) override; - Location emit(Function& function) override; - void format(stream& io) const override; - }; -} - -void write(stream& io, basil::ASTNode* t); -void write(stream& io, const basil::ASTNode* t); + class Def; + class Env; + + class ASTNode : public RC { + SourceLocation _loc; + const Type* _type; + + protected: + virtual const Type* lazy_type() = 0; + + public: + ASTNode(SourceLocation loc); + virtual ~ASTNode(); + + SourceLocation loc() const; + const Type* type(); + virtual bool is_extern() const; + virtual ref emit(ref& parent) = 0; + virtual Location emit(Function& function) = 0; + virtual void format(stream& io) const = 0; + }; + + class ASTSingleton : public ASTNode { + const Type* _type; + + protected: + const Type* lazy_type() override; + + public: + ASTSingleton(const Type* type); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTVoid : public ASTNode { + protected: + const Type* lazy_type() override; + + public: + ASTVoid(SourceLocation loc); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTInt : public ASTNode { + i64 _value; + + protected: + const Type* lazy_type() override; + + public: + ASTInt(SourceLocation loc, i64 value); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTSymbol : public ASTNode { + u64 _value; + + protected: + const Type* lazy_type() override; + + public: + ASTSymbol(SourceLocation loc, u64 value); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTString : public ASTNode { + string _value; + + protected: + const Type* lazy_type() override; + + public: + ASTString(SourceLocation, const string& value); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTBool : public ASTNode { + bool _value; + + protected: + const Type* lazy_type() override; + + public: + ASTBool(SourceLocation loc, bool value); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTVar : public ASTNode { + ref _env; + u64 _name; + + protected: + const Type* lazy_type() override; + + public: + ASTVar(SourceLocation loc, const ref env, u64 name); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTExtern : public ASTNode { + const Type* _type; + + protected: + const Type* lazy_type() override; + + public: + ASTExtern(SourceLocation loc, const Type* type); + + bool is_extern() const override; + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTUnary : public ASTNode { + protected: + ASTNode* _child; + + public: + ASTUnary(SourceLocation loc, ASTNode* child); + ~ASTUnary(); + }; + + class ASTBinary : public ASTNode { + protected: + ASTNode *_left, *_right; + + public: + ASTBinary(SourceLocation loc, ASTNode* left, ASTNode* right); + ~ASTBinary(); + }; + + enum ASTMathOp { AST_ADD, AST_SUB, AST_MUL, AST_DIV, AST_REM }; + + class ASTBinaryMath : public ASTBinary { + ASTMathOp _op; + + protected: + const Type* lazy_type() override; + + public: + ASTBinaryMath(SourceLocation loc, ASTMathOp op, ASTNode* left, ASTNode* right); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + enum ASTLogicOp { AST_AND, AST_OR, AST_XOR, AST_NOT }; + + class ASTBinaryLogic : public ASTBinary { + ASTLogicOp _op; + + protected: + const Type* lazy_type() override; + + public: + ASTBinaryLogic(SourceLocation loc, ASTLogicOp op, ASTNode* left, ASTNode* right); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTNot : public ASTUnary { + protected: + const Type* lazy_type() override; + + public: + ASTNot(SourceLocation loc, ASTNode* child); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + enum ASTEqualOp { AST_EQUAL, AST_INEQUAL }; + + class ASTBinaryEqual : public ASTBinary { + ASTEqualOp _op; + + protected: + const Type* lazy_type() override; + + public: + ASTBinaryEqual(SourceLocation loc, ASTEqualOp op, ASTNode* left, ASTNode* right); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + enum ASTRelOp { AST_LESS, AST_LESS_EQUAL, AST_GREATER, AST_GREATER_EQUAL }; + + class ASTBinaryRel : public ASTBinary { + ASTRelOp _op; + + protected: + const Type* lazy_type() override; + + public: + ASTBinaryRel(SourceLocation loc, ASTRelOp op, ASTNode* left, ASTNode* right); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTDefine : public ASTUnary { + ref _env; + u64 _name; + + protected: + const Type* lazy_type() override; + + public: + ASTDefine(SourceLocation loc, ref env, u64 name, ASTNode* value); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTCall : public ASTNode { + ASTNode* _func; + vector _args; + + protected: + const Type* lazy_type() override; + + public: + ASTCall(SourceLocation loc, ASTNode* func, const vector& args); + ~ASTCall(); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTIncompleteFn : public ASTNode { + const Type* _args; + i64 _name; + + protected: + const Type* lazy_type() override; + + public: + ASTIncompleteFn(SourceLocation loc, const Type* args, i64 name); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTFunction : public ASTNode { + ref _env; + const Type* _args_type; + vector _args; + ASTNode* _body; + i64 _name; + bool _emitted; + u32 _label; + ref _entry, _exit; + + protected: + const Type* lazy_type() override; + + public: + ASTFunction(SourceLocation loc, ref env, const Type* args_type, const vector& args, ASTNode* body, + i64 name = -1); + ~ASTFunction(); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + u32 label() const; + }; + + class ASTBlock : public ASTNode { + vector _exprs; + + protected: + const Type* lazy_type() override; + + public: + ASTBlock(SourceLocation loc, const vector& exprs); + ~ASTBlock(); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTIf : public ASTNode { + ASTNode *_cond, *_if_true, *_if_false; + + protected: + const Type* lazy_type() override; + + public: + ASTIf(SourceLocation loc, ASTNode* cond, ASTNode* if_true, ASTNode* if_false); + ~ASTIf(); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTWhile : public ASTNode { + ASTNode *_cond, *_body; + + protected: + const Type* lazy_type() override; + + public: + ASTWhile(SourceLocation loc, ASTNode* cond, ASTNode* body); + ~ASTWhile(); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTIsEmpty : public ASTUnary { + protected: + const Type* lazy_type() override; + + public: + ASTIsEmpty(SourceLocation loc, ASTNode* list); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTHead : public ASTUnary { + protected: + const Type* lazy_type() override; + + public: + ASTHead(SourceLocation loc, ASTNode* list); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTTail : public ASTUnary { + protected: + const Type* lazy_type() override; + + public: + ASTTail(SourceLocation loc, ASTNode* list); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTCons : public ASTBinary { + protected: + const Type* lazy_type() override; + + public: + ASTCons(SourceLocation loc, ASTNode* first, ASTNode* rest); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTLength : public ASTUnary { + protected: + const Type* lazy_type() override; + + public: + ASTLength(SourceLocation loc, ASTNode* child); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTDisplay : public ASTUnary { + protected: + const Type* lazy_type() override; + + public: + ASTDisplay(SourceLocation loc, ASTNode* node); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTNativeCall : public ASTNode { + const string _func_name; + const Type* _ret; + const vector _args; + const vector _arg_types; + + protected: + const Type* lazy_type() override; + + public: + ASTNativeCall(SourceLocation loc, const string& func_name, const Type* ret); + // TODO make args a variadic template instead of a vector + ASTNativeCall(SourceLocation loc, const string& func_name, const Type* ret, const vector& args, + const vector& arg_types); + ~ASTNativeCall(); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTAssign : public ASTUnary { + ref _env; + u64 _dest; + + protected: + const Type* lazy_type() override; + + public: + ASTAssign(SourceLocation loc, const ref env, u64 dest, ASTNode* src); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; + + class ASTAnnotate : public ASTNode { + ASTNode* _value; + const Type* _type; + + protected: + const Type* lazy_type() override; + + public: + ASTAnnotate(SourceLocation loc, ASTNode* value, const Type* type); + ~ASTAnnotate(); + + ref emit(ref& parent) override; + Location emit(Function& function) override; + void format(stream& io) const override; + }; +} // namespace basil + +void write(stream& io, basil::ASTNode* t); +void write(stream& io, const basil::ASTNode* t); #endif \ No newline at end of file diff --git a/compiler/builtin.cpp b/compiler/builtin.cpp index 698cf4a..4171196 100644 --- a/compiler/builtin.cpp +++ b/compiler/builtin.cpp @@ -40,26 +40,23 @@ namespace basil { #define ARG(n) args.get_product()[n] - Builtin ADD_INT, ADD_SYMBOL, SUB, MUL, DIV, REM, - AND, OR, XOR, NOT, - EQUALS, NOT_EQUALS, LESS, GREATER, LESS_EQUAL, GREATER_EQUAL, - IS_EMPTY, HEAD, TAIL, CONS, DICT_IN, DICT_NOT_IN, - DISPLAY, READ_LINE, READ_WORD, READ_INT, LENGTH, - AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, AT_MODULE, AT_DICT, AT_DICT_TYPE, AT_DICT_LIST, - STRCAT, SUBSTR, ANNOTATE, TYPEOF, TYPEDEF, LIST_TYPE, OF_TYPE_MACRO, - OF_TYPE, ASSIGN, IF; + Builtin ADD_INT, ADD_SYMBOL, SUB, MUL, DIV, REM, AND, OR, XOR, NOT, EQUALS, NOT_EQUALS, LESS, GREATER, LESS_EQUAL, + GREATER_EQUAL, IS_EMPTY, HEAD, TAIL, CONS, DICT_IN, DICT_NOT_IN, DISPLAY, READ_LINE, READ_WORD, READ_INT, + LENGTH, AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, AT_MODULE, AT_DICT, AT_DICT_TYPE, AT_DICT_LIST, + STRCAT, SUBSTR, ANNOTATE, TYPEOF, TYPEDEF, LIST_TYPE, OF_TYPE_MACRO, OF_TYPE, ASSIGN, IF; static void init_builtins() { ADD_INT = {find(find(INT, INT), INT), - [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() + ARG(1).get_int()); }, - [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryMath(ARG(0).loc(), AST_ADD, ARG(0).get_runtime(), ARG(1).get_runtime())); - }}; + [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() + ARG(1).get_int()); }, + [](ref env, const Value& args) -> Value { + return Value( + new ASTBinaryMath(ARG(0).loc(), AST_ADD, ARG(0).get_runtime(), ARG(1).get_runtime())); + }}; ADD_SYMBOL = {find(find(SYMBOL, SYMBOL), SYMBOL), - [](ref env, const Value& args) -> Value { - return Value(symbol_for(ARG(0).get_symbol()) + symbol_for(ARG(1).get_symbol()), SYMBOL); - }, - nullptr}; + [](ref env, const Value& args) -> Value { + return Value(symbol_for(ARG(0).get_symbol()) + symbol_for(ARG(1).get_symbol()), SYMBOL); + }, + nullptr}; SUB = {find(find(INT, INT), INT), [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() - ARG(1).get_int()); }, [](ref env, const Value& args) -> Value { @@ -122,52 +119,41 @@ namespace basil { [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() < ARG(1).get_int(), BOOL); }, [](ref env, const Value& args) -> Value { return Value(new ASTBinaryRel(ARG(0).loc(), AST_LESS, ARG(0).get_runtime(), ARG(1).get_runtime())); - } - }; + }}; LESS_EQUAL = { find(find(INT, INT), BOOL), [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() <= ARG(1).get_int(), BOOL); }, [](ref env, const Value& args) -> Value { return Value( new ASTBinaryRel(ARG(0).loc(), AST_LESS_EQUAL, ARG(0).get_runtime(), ARG(1).get_runtime())); - } - }; + }}; GREATER = { find(find(INT, INT), BOOL), [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() > ARG(1).get_int(), BOOL); }, [](ref env, const Value& args) -> Value { return Value(new ASTBinaryRel(ARG(0).loc(), AST_GREATER, ARG(0).get_runtime(), ARG(1).get_runtime())); - } - }; + }}; GREATER_EQUAL = { find(find(INT, INT), BOOL), [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() >= ARG(1).get_int(), BOOL); }, [](ref env, const Value& args) -> Value { return Value( new ASTBinaryRel(ARG(0).loc(), AST_GREATER_EQUAL, ARG(0).get_runtime(), ARG(1).get_runtime())); - } - }; - DICT_IN = { - find(find(ANY, find(ANY, ANY)), BOOL), - [](ref env, const Value& args) -> Value { - return Value(ARG(1).get_dict()[ARG(0)] ? true : false, BOOL); - }, - nullptr - }; - DICT_NOT_IN = { - find(find(ANY, find(ANY, ANY)), BOOL), - [](ref env, const Value& args) -> Value { - return Value(ARG(1).get_dict()[ARG(0)] ? false : true, BOOL); - }, - nullptr - }; - DISPLAY = { - find(find(ANY), VOID), - nullptr, - [](ref env, const Value& args) -> Value { - return Value(new ASTDisplay(ARG(0).loc(), ARG(0).get_runtime())); - } - }; + }}; + DICT_IN = {find(find(ANY, find(ANY, ANY)), BOOL), + [](ref env, const Value& args) -> Value { + return Value(ARG(1).get_dict()[ARG(0)] ? true : false, BOOL); + }, + nullptr}; + DICT_NOT_IN = {find(find(ANY, find(ANY, ANY)), BOOL), + [](ref env, const Value& args) -> Value { + return Value(ARG(1).get_dict()[ARG(0)] ? false : true, BOOL); + }, + nullptr}; + DISPLAY = {find(find(ANY), VOID), nullptr, + [](ref env, const Value& args) -> Value { + return Value(new ASTDisplay(ARG(0).loc(), ARG(0).get_runtime())); + }}; AT_INT = { find(find(ANY, INT), ANY), [](ref env, const Value& args) -> Value { @@ -214,110 +200,84 @@ namespace basil { }, nullptr // todo: runtime indexing }; - AT_ARRAY_TYPE = { - find(find(TYPE, INT), TYPE), - [](ref env, const Value& args) -> Value { - return Value(find(ARG(0).get_type(), ARG(1).get_int()), TYPE); - }, - nullptr - }; + AT_ARRAY_TYPE = {find(find(TYPE, INT), TYPE), + [](ref env, const Value& args) -> Value { + return Value(find(ARG(0).get_type(), ARG(1).get_int()), TYPE); + }, + nullptr}; AT_DYNARRAY_TYPE = { find(find(TYPE, VOID), TYPE), - [](ref env, const Value& args) -> Value { - return Value(find(ARG(0).get_type()), TYPE); - }, - nullptr - }; - AT_DICT = { - find(find(find(ANY, ANY), ANY), ANY), - [](ref env, const Value& args) -> Value { - return *(ARG(0).get_dict())[ARG(1)]; - }, - nullptr - }; - AT_DICT_LIST = { - find(find(find(ANY, ANY), find(ANY)), ANY), - [](ref env, const Value& args) -> Value { - vector vals; - for (const Value& key : to_vector(ARG(1))) - vals.push(*ARG(0).get_dict()[key]); - return Value(new ArrayValue(vals)); - }, - nullptr - }; - AT_DICT_TYPE = { - find(find(TYPE, TYPE), TYPE), - [](ref env, const Value& args) -> Value { - return Value(find(ARG(1).get_type(), ARG(0).get_type()), TYPE); - }, - nullptr - }; - AT_MODULE = { - find(find(MODULE, SYMBOL), ANY), - [](ref env, const Value& args) -> Value { - if (!ARG(0).get_module().has(ARG(1).get_symbol())) { - err(ARG(1).loc(), "Module does not contain member '", - symbol_for(ARG(1).get_symbol()), "'."); - return error(); - } - return ARG(0).get_module().entry(ARG(1).get_symbol()); - }, - nullptr - }; - ANNOTATE = { - find(find(ANY, TYPE), ANY), - [](ref env, const Value& args) -> Value { - if (!ARG(0).type()->coerces_to(ARG(1).get_type())) { - err(ARG(0).loc(), "Could not unify value of type '", ARG(0).type(), "' with type '", - ARG(1).get_type(), "'."); - return error(); - } - return cast(ARG(0), ARG(1).get_type()); - }, - [](ref env, const Value& args) -> Value { - return new ASTAnnotate(ARG(0).loc(), lower(ARG(0)).get_runtime(), ARG(1).get_type()); - }, - NO_AUTO_LOWER - }; + [](ref env, const Value& args) -> Value { return Value(find(ARG(0).get_type()), TYPE); }, + nullptr}; + AT_DICT = {find(find(find(ANY, ANY), ANY), ANY), + [](ref env, const Value& args) -> Value { return *(ARG(0).get_dict())[ARG(1)]; }, nullptr}; + AT_DICT_LIST = {find(find(find(ANY, ANY), find(ANY)), ANY), + [](ref env, const Value& args) -> Value { + vector vals; + for (const Value& key : to_vector(ARG(1))) vals.push(*ARG(0).get_dict()[key]); + return Value(new ArrayValue(vals)); + }, + nullptr}; + AT_DICT_TYPE = {find(find(TYPE, TYPE), TYPE), + [](ref env, const Value& args) -> Value { + return Value(find(ARG(1).get_type(), ARG(0).get_type()), TYPE); + }, + nullptr}; + AT_MODULE = {find(find(MODULE, SYMBOL), ANY), + [](ref env, const Value& args) -> Value { + if (!ARG(0).get_module().has(ARG(1).get_symbol())) { + err(ARG(1).loc(), "Module does not contain member '", symbol_for(ARG(1).get_symbol()), + "'."); + return error(); + } + return ARG(0).get_module().entry(ARG(1).get_symbol()); + }, + nullptr}; + ANNOTATE = {find(find(ANY, TYPE), ANY), + [](ref env, const Value& args) -> Value { + if (!ARG(0).type()->coerces_to(ARG(1).get_type())) { + err(ARG(0).loc(), "Could not unify value of type '", ARG(0).type(), "' with type '", + ARG(1).get_type(), "'."); + return error(); + } + return cast(ARG(0), ARG(1).get_type()); + }, + [](ref env, const Value& args) -> Value { + return new ASTAnnotate(ARG(0).loc(), lower(ARG(0)).get_runtime(), ARG(1).get_type()); + }, + NO_AUTO_LOWER}; TYPEOF = {find(find(ANY), TYPE), [](ref env, const Value& args) -> Value { return Value(ARG(0).type(), TYPE); }, nullptr}; - TYPEDEF = { - find(2), - [](ref env, const Value &args) -> Value { - return list_of(Value("def", SYMBOL), ARG(0), list_of(Value("#of", SYMBOL), list_of(Value("quote", SYMBOL), ARG(0)), ARG(1))); - }, - nullptr - }; + TYPEDEF = {find(2), + [](ref env, const Value& args) -> Value { + return list_of(Value("def", SYMBOL), ARG(0), + list_of(Value("#of", SYMBOL), list_of(Value("quote", SYMBOL), ARG(0)), ARG(1))); + }, + nullptr}; LIST_TYPE = { find(find(TYPE), TYPE), [](ref env, const Value& args) -> Value { return Value(find(ARG(0).get_type()), TYPE); }, nullptr}; - OF_TYPE_MACRO = { - find(2), - [](ref env, const Value& args) -> Value { - return list_of(Value("#of", SYMBOL), list_of(Value("quote", SYMBOL), ARG(0)), ARG(1)); - }, - nullptr - }; - OF_TYPE = { - find(find(SYMBOL, TYPE), TYPE), - [](ref env, const Value& args) -> Value { - return Value(find(symbol_for(ARG(0).get_symbol()), ARG(1).get_type()), TYPE); - }, - nullptr - }; - IF = { - find(find(BOOL, ANY, ANY), ANY), - [](ref env, const Value& args) -> Value { return eval(env, ARG(ARG(0).get_bool() ? 1 : 2)); }, - [](ref env, const Value& args) -> Value { - Value left = eval(env, ARG(1)), right = eval(env, ARG(2)); - if (left.is_error() || right.is_error()) return error(); - if (!left.is_runtime()) left = lower(left); - if (!right.is_runtime()) right = lower(right); - return new ASTIf(ARG(0).loc(), ARG(0).get_runtime(), left.get_runtime(), right.get_runtime()); - }, - NO_AUTO_LOWER - }; + OF_TYPE_MACRO = {find(2), + [](ref env, const Value& args) -> Value { + return list_of(Value("#of", SYMBOL), list_of(Value("quote", SYMBOL), ARG(0)), ARG(1)); + }, + nullptr}; + OF_TYPE = {find(find(SYMBOL, TYPE), TYPE), + [](ref env, const Value& args) -> Value { + return Value(find(symbol_for(ARG(0).get_symbol()), ARG(1).get_type()), TYPE); + }, + nullptr}; + IF = {find(find(BOOL, ANY, ANY), ANY), + [](ref env, const Value& args) -> Value { return eval(env, ARG(ARG(0).get_bool() ? 1 : 2)); }, + [](ref env, const Value& args) -> Value { + Value left = eval(env, ARG(1)), right = eval(env, ARG(2)); + if (left.is_error() || right.is_error()) return error(); + if (!left.is_runtime()) left = lower(left); + if (!right.is_runtime()) right = lower(right); + return new ASTIf(ARG(0).loc(), ARG(0).get_runtime(), left.get_runtime(), right.get_runtime()); + }, + NO_AUTO_LOWER}; } static bool inited = false; @@ -351,8 +311,10 @@ namespace basil { env->infix("in", Value(env, DICT_IN), 2, 60); // env->infix("not", Value(env, DICT_NOT_IN), 2, 60); env->def("display", Value(env, DISPLAY), 1); - env->infix("at", cases(env, &AT_INT, &AT_LIST, &AT_ARRAY_TYPE, &AT_DYNARRAY_TYPE, - &AT_MODULE, &AT_DICT, &AT_DICT_TYPE, &AT_DICT_LIST), 2, 120); + env->infix("at", + cases(env, &AT_INT, &AT_LIST, &AT_ARRAY_TYPE, &AT_DYNARRAY_TYPE, &AT_MODULE, &AT_DICT, &AT_DICT_TYPE, + &AT_DICT_LIST), + 2, 120); env->def("annotate", Value(env, ANNOTATE), 2); env->def("typeof", Value(env, TYPEOF), 1); env->def_macro("typedef", Value(env, TYPEDEF), 2); diff --git a/compiler/builtin.h b/compiler/builtin.h index 9cc9dea..038f718 100644 --- a/compiler/builtin.h +++ b/compiler/builtin.h @@ -10,22 +10,20 @@ namespace basil { class Value; class Env; - enum BuiltinFlags { - AUTO_LOWER = 0, - NO_AUTO_LOWER = 1 - }; + enum BuiltinFlags { AUTO_LOWER = 0, NO_AUTO_LOWER = 1 }; class Builtin { - using Function = Value(*)(ref, const Value&); + using Function = Value (*)(ref, const Value&); const Type* _type; Function _eval, _compile; u32 _flags; vector _args; - public: + + public: Builtin(); Builtin(const Type* type, Function eval, Function compile, BuiltinFlags flags = AUTO_LOWER); - Builtin(const Type* type, Function eval, Function compile, - const vector& args, BuiltinFlags flags = AUTO_LOWER); + Builtin(const Type* type, Function eval, Function compile, const vector& args, + BuiltinFlags flags = AUTO_LOWER); const Type* type() const; const vector& args() const; @@ -37,15 +35,11 @@ namespace basil { extern void define_builtins(ref env); - extern Builtin - ADD_INT, ADD_SYMBOL, SUB, MUL, DIV, REM, - AND, OR, XOR, NOT, - EQUALS, NOT_EQUALS, LESS, GREATER, LESS_EQUAL, GREATER_EQUAL, - IS_EMPTY, HEAD, TAIL, CONS, DICT_PUT, DICT_IN, DICT_NOT_IN, DICT_REMOVE, - DISPLAY, READ_LINE, READ_WORD, READ_INT, - LENGTH, AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, AT_DICT_TYPE, AT_MODULE, AT_DICT, AT_DICT_LIST, - STRCAT, SUBSTR, ANNOTATE, TYPEOF, TYPEDEF, LIST_TYPE, OF_TYPE_MACRO, OF_TYPE, + extern Builtin ADD_INT, ADD_SYMBOL, SUB, MUL, DIV, REM, AND, OR, XOR, NOT, EQUALS, NOT_EQUALS, LESS, GREATER, + LESS_EQUAL, GREATER_EQUAL, IS_EMPTY, HEAD, TAIL, CONS, DICT_PUT, DICT_IN, DICT_NOT_IN, DICT_REMOVE, DISPLAY, + READ_LINE, READ_WORD, READ_INT, LENGTH, AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, AT_DICT_TYPE, + AT_MODULE, AT_DICT, AT_DICT_LIST, STRCAT, SUBSTR, ANNOTATE, TYPEOF, TYPEDEF, LIST_TYPE, OF_TYPE_MACRO, OF_TYPE, ASSIGN, IF; -} +} // namespace basil #endif \ No newline at end of file diff --git a/compiler/driver.cpp b/compiler/driver.cpp index fffded1..7eaea1a 100644 --- a/compiler/driver.cpp +++ b/compiler/driver.cpp @@ -1,298 +1,293 @@ #include "driver.h" -#include "lex.h" -#include "parse.h" +#include "ast.h" #include "errors.h" -#include "values.h" -#include "native.h" #include "eval.h" -#include "ast.h" +#include "lex.h" +#include "native.h" +#include "parse.h" #include "util/io.h" +#include "values.h" namespace basil { - static bool _print_tokens = false, - _print_parsed = false, - _print_ast = false, - _print_ir = false, - _print_asm = false, - _compile_only = false; - - void print_tokens(bool should) { - _print_tokens = should; - } - - void print_parsed(bool should) { - _print_parsed = should; - } - - void print_ast(bool should) { - _print_ast = should; - } - - void print_ir(bool should) { - _print_ir = should; - } - - void print_asm(bool should) { - _print_asm = should; - } - - void compile_only(bool should) { - _compile_only = should; - } - - bool is_compile_only() { - return _compile_only; - } - - vector lex(Source::View& view) { - vector tokens; - while (view.peek()) tokens.push(scan(view)); - - if (_print_tokens) { - print(BOLDYELLOW); - for (const Token& tok : tokens) print(tok, " "); - println(RESET, "\n"); - } - return tokens; - } - - Value parse(TokenView& view) { - vector results; - TokenView end = view; - while (end) end.read(); - while (view.peek()) { - Value line = parse_line(view, view.peek().column); - if (!line.is_void()) results.push(line); - } - if (_print_tokens) { - print(BOLDYELLOW); - while (end) print(end.read(), " "); - println(RESET); - } - if (_print_parsed) { - print(BOLDGREEN); - for (const Value& v : results) println(v); - println(RESET); - } - return cons(string("do"), list_of(results)); - } - - ref create_global_env() { - ref root = create_root_env(); - ref global_env = newref(root); - return global_env; - } - - void compile(Value value, Object& object, Function& fn) { - fn.allocate(); - fn.emit(object); - emit_constants(object); - if (error_count()) return; - - if (_print_asm) { - print(BOLDRED); - byte_buffer code = object.code(); - while (code.size()) printf("%02x ", code.read()); - println(RESET, "\n"); - } - } - - void generate(Value value, Function& fn) { - Location last = loc_none(); - if (value.is_runtime()) last = value.get_runtime()->emit(fn); - if (last.type != LOC_NONE) fn.add(new RetInsn(last)); - if (error_count()) return; - - if (_print_ir) { - print(BOLDMAGENTA); - print(fn); - println(RESET, "\n"); - } - } - - void compile(Value value, Object& object) { - Function main_fn("_start"); - Location last = loc_none(); - if (value.is_runtime()) last = value.get_runtime()->emit(main_fn); - if (last.type != LOC_NONE) main_fn.add(new RetInsn(last)); - if (error_count()) return; - - if (_print_ir) { - print(BOLDMAGENTA); - print(main_fn); - println(RESET, "\n"); - } - - compile(value, object, main_fn); - } - - void jit_print(Value value, const Object& object) { - auto main_jit = object.find(jasmine::global("main")); - if (main_jit) { - u64 result = ((u64(*)())main_jit)(); - const Type* t = value.get_runtime()->type(); - if (t == VOID) return; - print("= "); - - if (t == INT) print((i64)result); - else if (t == SYMBOL) print(symbol_for(result)); - else if (t == BOOL) print((bool)result); - else if (t == STRING) print('"', (const char*)result, '"'); - else if (t->kind() == KIND_LIST) display_native_list(t, (void*)result); - println(""); - } - } - - int execute(Value value, const Object& object) { - auto main_jit = object.find(jasmine::global("_start")); - if (main_jit) return ((i64(*)())main_jit)(); - return 1; - } - - Value repl(ref global, Source& src, Function& mainfn) { - print("? "); - auto view = src.expand(_stdin); - auto tokens = lex(view); - if (error_count()) return print_errors(_stdout, src), error(); - - TokenView tview(tokens, src, true); - Value program = parse(tview); - if (error_count()) return print_errors(_stdout, src), error(); - - prep(global, program); - Value result = eval(global, program); - if (error_count()) return print_errors(_stdout, src), error(); - - if (!result.is_runtime()) { - if (!result.is_void()) - println(BOLDBLUE, "= ", result, RESET, "\n"); - return result; - } - if (_print_ast) - println(BOLDCYAN, result.get_runtime(), RESET, "\n"); - - jasmine::Object object; - generate(result, mainfn); - compile(result, object, mainfn); - add_native_functions(object); - object.load(); - if (error_count()) return print_errors(_stdout, src), error(); - - print(BOLDBLUE); - jit_print(result, object); - println(RESET); - return result; - } - - ref load(Source& src) { - auto view = src.begin(); - auto tokens = lex(view); - if (error_count()) return print_errors(_stdout, src), nullptr; - - TokenView tview(tokens, src); - Value program = parse(tview); - if (error_count()) return print_errors(_stdout, src), nullptr; - - ref global = create_global_env(); - - prep(global, program); - Value result = eval(global, program); - if (error_count()) return print_errors(_stdout, src), nullptr; - - return global; - } - - int run(Source& src) { - auto view = src.begin(); - auto tokens = lex(view); - if (error_count()) return print_errors(_stdout, src), 1; - - TokenView tview(tokens, src); - Value program = parse(tview); - if (error_count()) return print_errors(_stdout, src), 1; - - ref global = create_global_env(); - - prep(global, program); - Value result = eval(global, program); - if (error_count()) return print_errors(_stdout, src), 1; - - if (!result.is_runtime()) return 0; - if (_print_ast) - println(BOLDCYAN, result.get_runtime(), RESET, "\n"); - - jasmine::Object object; - compile(result, object); - add_native_functions(object); - object.load(); - if (error_count()) return print_errors(_stdout, src), 1; - - return execute(result, object); - } - - string change_ending(string s, string e) { - string d = ""; - int last = 0; - for (int i = 0; i < s.size(); i ++) if (s[i] == '.') last = i; - for (int i = 0; i < last; i ++) d += s[i]; - return d + e; - } - - vector instantiations(ref env) { - vector insts; - for (const auto& p : *env) { - if (p.second.value.is_function()) { - const FunctionValue& fn = p.second.value.get_function(); - if (fn.instantiations()) for (auto& i : *fn.instantiations()) { - insts.push(i.second); - } - } - } - return insts; - } - - int build(Source& src, const char* filename) { - string dest = change_ending(filename, ".o"); - auto view = src.begin(); - auto tokens = lex(view); - if (error_count()) return print_errors(_stdout, src), 1; - - TokenView tview(tokens, src); - Value program = parse(tview); - if (error_count()) return print_errors(_stdout, src), 1; - - ref global = create_global_env(); - - prep(global, program); - Value result = eval(global, program); - if (error_count()) return print_errors(_stdout, src), 1; - - auto insts = instantiations(global); - if (result.is_runtime()) { - if (_print_ast) - println(BOLDCYAN, result.get_runtime(), RESET, "\n"); - - jasmine::Object object; - compile(result, object); - if (error_count()) return print_errors(_stdout, src), 1; - object.writeELF((const char*)dest.raw()); - } - else if (insts.size() > 0) { - jasmine::Object object; - Function container(string(".") + filename); - for (Value f : insts) f.get_runtime()->emit(container); - if (_print_ir) { - print(BOLDMAGENTA); - print(container); - println(RESET, "\n"); - } - container.allocate(); - container.emit(object, true); - emit_constants(object); - if (error_count()) return print_errors(_stdout, src), 1; - object.writeELF((const char*)dest.raw()); - } - - return 0; - } -} \ No newline at end of file + static bool _print_tokens = false, _print_parsed = false, _print_ast = false, _print_ir = false, _print_asm = false, + _compile_only = false; + + void print_tokens(bool should) { + _print_tokens = should; + } + + void print_parsed(bool should) { + _print_parsed = should; + } + + void print_ast(bool should) { + _print_ast = should; + } + + void print_ir(bool should) { + _print_ir = should; + } + + void print_asm(bool should) { + _print_asm = should; + } + + void compile_only(bool should) { + _compile_only = should; + } + + bool is_compile_only() { + return _compile_only; + } + + vector lex(Source::View& view) { + vector tokens; + while (view.peek()) tokens.push(scan(view)); + + if (_print_tokens) { + print(BOLDYELLOW); + for (const Token& tok : tokens) print(tok, " "); + println(RESET, "\n"); + } + return tokens; + } + + Value parse(TokenView& view) { + vector results; + TokenView end = view; + while (end) end.read(); + while (view.peek()) { + Value line = parse_line(view, view.peek().column); + if (!line.is_void()) results.push(line); + } + if (_print_tokens) { + print(BOLDYELLOW); + while (end) print(end.read(), " "); + println(RESET); + } + if (_print_parsed) { + print(BOLDGREEN); + for (const Value& v : results) println(v); + println(RESET); + } + return cons(string("do"), list_of(results)); + } + + ref create_global_env() { + ref root = create_root_env(); + ref global_env = newref(root); + return global_env; + } + + void compile(Value value, Object& object, Function& fn) { + fn.allocate(); + fn.emit(object); + emit_constants(object); + if (error_count()) return; + + if (_print_asm) { + print(BOLDRED); + byte_buffer code = object.code(); + while (code.size()) printf("%02x ", code.read()); + println(RESET, "\n"); + } + } + + void generate(Value value, Function& fn) { + Location last = loc_none(); + if (value.is_runtime()) last = value.get_runtime()->emit(fn); + if (last.type != LOC_NONE) fn.add(new RetInsn(last)); + if (error_count()) return; + + if (_print_ir) { + print(BOLDMAGENTA); + print(fn); + println(RESET, "\n"); + } + } + + void compile(Value value, Object& object) { + Function main_fn("_start"); + Location last = loc_none(); + if (value.is_runtime()) last = value.get_runtime()->emit(main_fn); + if (last.type != LOC_NONE) main_fn.add(new RetInsn(last)); + if (error_count()) return; + + if (_print_ir) { + print(BOLDMAGENTA); + print(main_fn); + println(RESET, "\n"); + } + + compile(value, object, main_fn); + } + + void jit_print(Value value, const Object& object) { + auto main_jit = object.find(jasmine::global("main")); + if (main_jit) { + u64 result = ((u64(*)())main_jit)(); + const Type* t = value.get_runtime()->type(); + if (t == VOID) return; + print("= "); + + if (t == INT) print((i64)result); + else if (t == SYMBOL) + print(symbol_for(result)); + else if (t == BOOL) + print((bool)result); + else if (t == STRING) + print('"', (const char*)result, '"'); + else if (t->kind() == KIND_LIST) + display_native_list(t, (void*)result); + println(""); + } + } + + int execute(Value value, const Object& object) { + auto main_jit = object.find(jasmine::global("_start")); + if (main_jit) return ((i64(*)())main_jit)(); + return 1; + } + + Value repl(ref global, Source& src, Function& mainfn) { + print("? "); + auto view = src.expand(_stdin); + auto tokens = lex(view); + if (error_count()) return print_errors(_stdout, src), error(); + + TokenView tview(tokens, src, true); + Value program = parse(tview); + if (error_count()) return print_errors(_stdout, src), error(); + + prep(global, program); + Value result = eval(global, program); + if (error_count()) return print_errors(_stdout, src), error(); + + if (!result.is_runtime()) { + if (!result.is_void()) println(BOLDBLUE, "= ", result, RESET, "\n"); + return result; + } + if (_print_ast) println(BOLDCYAN, result.get_runtime(), RESET, "\n"); + + jasmine::Object object; + generate(result, mainfn); + compile(result, object, mainfn); + add_native_functions(object); + object.load(); + if (error_count()) return print_errors(_stdout, src), error(); + + print(BOLDBLUE); + jit_print(result, object); + println(RESET); + return result; + } + + ref load(Source& src) { + auto view = src.begin(); + auto tokens = lex(view); + if (error_count()) return print_errors(_stdout, src), nullptr; + + TokenView tview(tokens, src); + Value program = parse(tview); + if (error_count()) return print_errors(_stdout, src), nullptr; + + ref global = create_global_env(); + + prep(global, program); + Value result = eval(global, program); + if (error_count()) return print_errors(_stdout, src), nullptr; + + return global; + } + + int run(Source& src) { + auto view = src.begin(); + auto tokens = lex(view); + if (error_count()) return print_errors(_stdout, src), 1; + + TokenView tview(tokens, src); + Value program = parse(tview); + if (error_count()) return print_errors(_stdout, src), 1; + + ref global = create_global_env(); + + prep(global, program); + Value result = eval(global, program); + if (error_count()) return print_errors(_stdout, src), 1; + + if (!result.is_runtime()) return 0; + if (_print_ast) println(BOLDCYAN, result.get_runtime(), RESET, "\n"); + + jasmine::Object object; + compile(result, object); + add_native_functions(object); + object.load(); + if (error_count()) return print_errors(_stdout, src), 1; + + return execute(result, object); + } + + string change_ending(string s, string e) { + string d = ""; + int last = 0; + for (int i = 0; i < s.size(); i++) + if (s[i] == '.') last = i; + for (int i = 0; i < last; i++) d += s[i]; + return d + e; + } + + vector instantiations(ref env) { + vector insts; + for (const auto& p : *env) { + if (p.second.value.is_function()) { + const FunctionValue& fn = p.second.value.get_function(); + if (fn.instantiations()) + for (auto& i : *fn.instantiations()) { insts.push(i.second); } + } + } + return insts; + } + + int build(Source& src, const char* filename) { + string dest = change_ending(filename, ".o"); + auto view = src.begin(); + auto tokens = lex(view); + if (error_count()) return print_errors(_stdout, src), 1; + + TokenView tview(tokens, src); + Value program = parse(tview); + if (error_count()) return print_errors(_stdout, src), 1; + + ref global = create_global_env(); + + prep(global, program); + Value result = eval(global, program); + if (error_count()) return print_errors(_stdout, src), 1; + + auto insts = instantiations(global); + if (result.is_runtime()) { + if (_print_ast) println(BOLDCYAN, result.get_runtime(), RESET, "\n"); + + jasmine::Object object; + compile(result, object); + if (error_count()) return print_errors(_stdout, src), 1; + object.writeELF((const char*)dest.raw()); + } else if (insts.size() > 0) { + jasmine::Object object; + Function container(string(".") + filename); + for (Value f : insts) f.get_runtime()->emit(container); + if (_print_ir) { + print(BOLDMAGENTA); + print(container); + println(RESET, "\n"); + } + container.allocate(); + container.emit(object, true); + emit_constants(object); + if (error_count()) return print_errors(_stdout, src), 1; + object.writeELF((const char*)dest.raw()); + } + + return 0; + } +} // namespace basil \ No newline at end of file diff --git a/compiler/driver.h b/compiler/driver.h index 99e520e..a72eede 100644 --- a/compiler/driver.h +++ b/compiler/driver.h @@ -1,24 +1,24 @@ #ifndef BASIL_DRIVER_H #define BASIL_DRIVER_H -#include "source.h" #include "env.h" #include "ir.h" +#include "source.h" #include "values.h" namespace basil { - void print_tokens(bool should); - void print_parsed(bool should); - void print_ast(bool should); - void print_ir(bool should); - void print_asm(bool should); - void compile_only(bool should); - bool is_compile_only(); + void print_tokens(bool should); + void print_parsed(bool should); + void print_ast(bool should); + void print_ir(bool should); + void print_asm(bool should); + void compile_only(bool should); + bool is_compile_only(); - Value repl(ref global, Source& src, Function& mainfn); - ref load(Source& src); - int run(Source& src); - int build(Source& src, const char* dest); -} + Value repl(ref global, Source& src, Function& mainfn); + ref load(Source& src); + int run(Source& src); + int build(Source& src, const char* dest); +} // namespace basil #endif \ No newline at end of file diff --git a/compiler/env.cpp b/compiler/env.cpp index 388044c..b7f7cd7 100644 --- a/compiler/env.cpp +++ b/compiler/env.cpp @@ -1,136 +1,129 @@ #include "env.h" namespace basil { - Def::Def(bool is_macro_in, bool is_procedure_in, bool is_infix_in, - u8 arity_in, u8 precedence_in): - Def(Value(), is_macro_in, is_procedure_in, is_infix_in, - arity_in, precedence_in) {} - - Def::Def(Value value_in, bool is_macro_in, bool is_procedure_in, - bool is_infix_in, u8 arity_in, u8 precedence_in): - value(value_in), is_macro(is_macro_in), - is_proc(is_procedure_in), is_infix(is_infix_in), - arity(arity_in), precedence(precedence_in) {} - - bool Def::is_procedure() const { - return is_proc && !is_macro; - } - - bool Def::is_variable() const { - return !is_proc && !is_macro; - } - - bool Def::is_macro_procedure() const { - return is_proc && is_macro; - } - - bool Def::is_macro_variable() const { - return !is_proc && is_macro; - } - - Env::Env(const ref& parent): - _parent(parent), _runtime(false) {} - - void Env::def(const string& name) { - _defs[name] = Def(false, false, false); - } - - void Env::def_macro(const string& name) { - _defs[name] = Def(true, false, false); - } - - void Env::def(const string& name, u8 arity) { - _defs[name] = Def(false, true, false, arity); - } - - void Env::def_macro(const string& name, u8 arity) { - _defs[name] = Def(true, true, false, arity); - } - - void Env::def(const string& name, const Value& value) { - _defs[name] = Def(value, false, false, false); - } - - void Env::def(const string& name, const Value& value, u8 arity) { - _defs[name] = Def(value, false, true, false, arity); - } - - void Env::def_macro(const string& name, const Value& value) { - _defs[name] = Def(value, true, false, false); - } - - void Env::def_macro(const string& name, const Value& value, u8 arity) { - _defs[name] = Def(value, true, true, false, arity); - } - - void Env::infix(const string& name, u8 arity, u8 precedence) { - _defs[name] = Def(false, true, true, arity, precedence); - } - - void Env::infix(const string& name, const Value& value, u8 arity, u8 precedence) { - _defs[name] = Def(value, false, true, true, arity, precedence); - } - - void Env::infix_macro(const string& name, u8 arity, u8 precedence) { - _defs[name] = Def(true, true, true, arity, precedence); - } - - void Env::infix_macro(const string& name, const Value& value, - u8 arity, u8 precedence) { - _defs[name] = Def(value, true, true, true, arity, precedence); - } - - const Def* Env::find(const string& name) const { - auto it = _defs.find(name); - if (it == _defs.end()) - return _parent ? _parent->find(name) : nullptr; - else return &it->second; - } - - Def* Env::find(const string& name) { - auto it = _defs.find(name); - if (it == _defs.end()) - return _parent ? _parent->find(name) : nullptr; - else return &it->second; - } - - void Env::format(stream& io) const { - write(io, "{"); - bool first = true; - for (auto& def : _defs) - write(io, first ? "" : " ", def.first), first = false; - write(io, "}"); - if (_parent) write(io, " -> "), _parent->format(io); - } - - ref Env::clone() const { - ref new_ref = newref(_parent); - for (auto& p : _defs) new_ref->_defs.put(p.first, p.second); - new_ref->_runtime = _runtime; - return new_ref; - } - - map::const_iterator Env::begin() const { - return _defs.begin(); - } - - map::const_iterator Env::end() const { - return _defs.end(); - } - - void Env::import(ref env) { - for (auto& p : env->_defs) _defs.put(p.first, p.second); - } - - void Env::import_single(const string &name, const Def &d) { - _defs.put(name, d); - } - - void Env::make_runtime() { - _runtime = true; - } - - bool Env::is_runtime() const { - return _runtime; - } -} \ No newline at end of file + Def::Def(bool is_macro_in, bool is_procedure_in, bool is_infix_in, u8 arity_in, u8 precedence_in) + : Def(Value(), is_macro_in, is_procedure_in, is_infix_in, arity_in, precedence_in) {} + + Def::Def(Value value_in, bool is_macro_in, bool is_procedure_in, bool is_infix_in, u8 arity_in, u8 precedence_in) + : value(value_in), is_macro(is_macro_in), is_proc(is_procedure_in), is_infix(is_infix_in), arity(arity_in), + precedence(precedence_in) {} + + bool Def::is_procedure() const { + return is_proc && !is_macro; + } + + bool Def::is_variable() const { + return !is_proc && !is_macro; + } + + bool Def::is_macro_procedure() const { + return is_proc && is_macro; + } + + bool Def::is_macro_variable() const { + return !is_proc && is_macro; + } + + Env::Env(const ref& parent) : _parent(parent), _runtime(false) {} + + void Env::def(const string& name) { + _defs[name] = Def(false, false, false); + } + + void Env::def_macro(const string& name) { + _defs[name] = Def(true, false, false); + } + + void Env::def(const string& name, u8 arity) { + _defs[name] = Def(false, true, false, arity); + } + + void Env::def_macro(const string& name, u8 arity) { + _defs[name] = Def(true, true, false, arity); + } + + void Env::def(const string& name, const Value& value) { + _defs[name] = Def(value, false, false, false); + } + + void Env::def(const string& name, const Value& value, u8 arity) { + _defs[name] = Def(value, false, true, false, arity); + } + + void Env::def_macro(const string& name, const Value& value) { + _defs[name] = Def(value, true, false, false); + } + + void Env::def_macro(const string& name, const Value& value, u8 arity) { + _defs[name] = Def(value, true, true, false, arity); + } + + void Env::infix(const string& name, u8 arity, u8 precedence) { + _defs[name] = Def(false, true, true, arity, precedence); + } + + void Env::infix(const string& name, const Value& value, u8 arity, u8 precedence) { + _defs[name] = Def(value, false, true, true, arity, precedence); + } + + void Env::infix_macro(const string& name, u8 arity, u8 precedence) { + _defs[name] = Def(true, true, true, arity, precedence); + } + + void Env::infix_macro(const string& name, const Value& value, u8 arity, u8 precedence) { + _defs[name] = Def(value, true, true, true, arity, precedence); + } + + const Def* Env::find(const string& name) const { + auto it = _defs.find(name); + if (it == _defs.end()) return _parent ? _parent->find(name) : nullptr; + else + return &it->second; + } + + Def* Env::find(const string& name) { + auto it = _defs.find(name); + if (it == _defs.end()) return _parent ? _parent->find(name) : nullptr; + else + return &it->second; + } + + void Env::format(stream& io) const { + write(io, "{"); + bool first = true; + for (auto& def : _defs) write(io, first ? "" : " ", def.first), first = false; + write(io, "}"); + if (_parent) write(io, " -> "), _parent->format(io); + } + + ref Env::clone() const { + ref new_ref = newref(_parent); + for (auto& p : _defs) new_ref->_defs.put(p.first, p.second); + new_ref->_runtime = _runtime; + return new_ref; + } + + map::const_iterator Env::begin() const { + return _defs.begin(); + } + + map::const_iterator Env::end() const { + return _defs.end(); + } + + void Env::import(ref env) { + for (auto& p : env->_defs) _defs.put(p.first, p.second); + } + + void Env::import_single(const string& name, const Def& d) { + _defs.put(name, d); + } + + void Env::make_runtime() { + _runtime = true; + } + + bool Env::is_runtime() const { + return _runtime; + } +} // namespace basil \ No newline at end of file diff --git a/compiler/env.h b/compiler/env.h index 9c6b405..7849e8b 100644 --- a/compiler/env.h +++ b/compiler/env.h @@ -1,66 +1,66 @@ #ifndef BASIL_ENV_H #define BASIL_ENV_H +#include "ir.h" +#include "ssa.h" #include "util/defs.h" #include "util/hash.h" -#include "util/str.h" #include "util/rc.h" +#include "util/str.h" #include "values.h" -#include "ssa.h" -#include "ir.h" namespace basil { - struct Def { - Value value; - bool is_macro; // is the definition a macro alias or procedure? - bool is_infix; // is the definition for an infix procedure? - bool is_proc; // is the definition a scalar or procedure? - u8 arity; // number of arguments taken by a procedure. - u8 precedence; // precedence of infix procedure - SSAIdent ident; - Location location; + struct Def { + Value value; + bool is_macro; // is the definition a macro alias or procedure? + bool is_infix; // is the definition for an infix procedure? + bool is_proc; // is the definition a scalar or procedure? + u8 arity; // number of arguments taken by a procedure. + u8 precedence; // precedence of infix procedure + SSAIdent ident; + Location location; + + Def(bool is_macro_in = false, bool is_procedure_in = false, bool is_infix_in = false, u8 arity_in = 0, + u8 precedence_in = 0); + Def(Value value_in, bool is_procedure_in = false, bool is_macro_in = false, bool is_infix_in = false, + u8 arity_in = 0, u8 precedence_in = 0); + bool is_procedure() const; + bool is_variable() const; + bool is_macro_procedure() const; + bool is_macro_variable() const; + }; - Def(bool is_macro_in = false, bool is_procedure_in = false, - bool is_infix_in = false, u8 arity_in = 0, u8 precedence_in = 0); - Def(Value value_in, bool is_procedure_in = false, - bool is_macro_in = false, bool is_infix_in = false, - u8 arity_in = 0, u8 precedence_in = 0); - bool is_procedure() const; - bool is_variable() const; - bool is_macro_procedure() const; - bool is_macro_variable() const; - }; + class Env { + map _defs; + ref _parent; + bool _runtime; - class Env { - map _defs; - ref _parent; - bool _runtime; - public: - Env(const ref& parent = nullptr); + public: + Env(const ref& parent = nullptr); - void def(const string& name); - void def(const string& name, u8 arity); - void def_macro(const string& name); - void def_macro(const string& name, u8 arity); - void def(const string& name, const Value& value); - void def(const string& name, const Value& value, u8 arity); - void def_macro(const string& name, const Value& value); - void def_macro(const string& name, const Value& value, u8 arity); - void infix(const string& name, u8 arity, u8 precedence); - void infix(const string& name, const Value& value, u8 arity, u8 precedence); - void infix_macro(const string& name, u8 arity, u8 precedence); - void infix_macro(const string& name, const Value& value, u8 arity, u8 precedence); - const Def* find(const string& name) const; - Def* find(const string& name); - void format(stream& io) const; - ref clone() const; - map::const_iterator begin() const; - map::const_iterator end() const; - void import(ref env); - void import_single(const string &name, const Def &d); - void make_runtime(); - bool is_runtime() const; - }; -} + void def(const string& name); + void def(const string& name, u8 arity); + void def_macro(const string& name); + void def_macro(const string& name, u8 arity); + void def(const string& name, const Value& value); + void def(const string& name, const Value& value, u8 arity); + void def_macro(const string& name, const Value& value); + void def_macro(const string& name, const Value& value, u8 arity); + void infix(const string& name, u8 arity, u8 precedence); + void infix(const string& name, const Value& value, u8 arity, u8 precedence); + void infix_macro(const string& name, u8 arity, u8 precedence); + void infix_macro(const string& name, const Value& value, u8 arity, u8 precedence); + const Def* find(const string& name) const; + Def* find(const string& name); + void format(stream& io) const; + ref clone() const; + map::const_iterator begin() const; + map::const_iterator end() const; + void import(ref env); + void import_single(const string& name, const Def& d); + void make_runtime(); + bool is_runtime() const; + }; +} // namespace basil #endif \ No newline at end of file diff --git a/compiler/errors.cpp b/compiler/errors.cpp index 9b13d8a..343eda4 100644 --- a/compiler/errors.cpp +++ b/compiler/errors.cpp @@ -1,73 +1,71 @@ #include "errors.h" -#include "util/vec.h" #include "lex.h" +#include "util/vec.h" namespace basil { - SourceLocation::SourceLocation(): - line(0), column(0), length(0) {} + SourceLocation::SourceLocation() : line(0), column(0), length(0) {} - SourceLocation::SourceLocation(u32 line_in, u16 column_in, u16 length_in): - line(line_in), column(column_in), length(length_in) {} + SourceLocation::SourceLocation(u32 line_in, u16 column_in, u16 length_in) + : line(line_in), column(column_in), length(length_in) {} - SourceLocation::SourceLocation(const Token& token): - line(token.line), column(token.column), length(token.value.size()) {} + SourceLocation::SourceLocation(const Token& token) + : line(token.line), column(token.column), length(token.value.size()) {} - const SourceLocation NO_LOCATION = { 0, 0, 0 }; + const SourceLocation NO_LOCATION = {0, 0, 0}; - struct Error { - SourceLocation loc; - string message; - }; + struct Error { + SourceLocation loc; + string message; + }; - static vector errors; - static i64 silenced = 0; + static vector errors; + static i64 silenced = 0; - void err(SourceLocation loc, const string& message) { - if (!silenced) errors.push({ loc, message }); - } + void err(SourceLocation loc, const string& message) { + if (!silenced) errors.push({loc, message}); + } - u32 error_count() { - return errors.size(); - } + u32 error_count() { + return errors.size(); + } - void clear_errors() { - errors.clear(); - } + void clear_errors() { + errors.clear(); + } - void print_errors(stream& io) { - for (const Error& e : errors) { - if (e.loc.length != 0) - write(io, "[", e.loc.line + 1, ":", e.loc.column + 1, "] "); - writeln(io, e.message); + void print_errors(stream& io) { + for (const Error& e : errors) { + if (e.loc.length != 0) write(io, "[", e.loc.line + 1, ":", e.loc.column + 1, "] "); + writeln(io, e.message); + } + clear_errors(); } - clear_errors(); - } - void print_errors(stream& io, const Source& src) { - for (const Error& e : errors) { - if (e.loc.length != 0) - write(io, "[", e.loc.line + 1, ":", e.loc.column + 1, "] "); - writeln(io, e.message); + void print_errors(stream& io, const Source& src) { + for (const Error& e : errors) { + if (e.loc.length != 0) write(io, "[", e.loc.line + 1, ":", e.loc.column + 1, "] "); + writeln(io, e.message); + + if (e.loc.length == 0) continue; // skip source printing if no location + const auto& line = src.line(e.loc.line); + if (line.back() == '\n') write(io, '\t', line); + else + writeln(io, '\t', line); + u32 first = e.loc.column, last = e.loc.column + e.loc.length; + u32 i = 0; + write(io, '\t'); + for (; i < first; i++) write(io, ' '); + for (; i < last; i++) write(io, '^'); + writeln(io, ""); + } + } - if (e.loc.length == 0) continue; // skip source printing if no location - const auto& line = src.line(e.loc.line); - if (line.back() == '\n') write(io, '\t', line); - else writeln(io, '\t', line); - u32 first = e.loc.column, last = e.loc.column + e.loc.length; - u32 i = 0; - write(io, '\t'); - for (; i < first; i ++) write(io, ' '); - for (; i < last; i ++) write(io, '^'); - writeln(io, ""); + void silence_errors() { + silenced++; } - } - - void silence_errors() { - silenced ++; - } - void unsilence_errors() { - silenced --; - if (silenced < 0) silenced = 0; - } -} \ No newline at end of file + void unsilence_errors() { + silenced--; + if (silenced < 0) silenced = 0; + } +} // namespace basil \ No newline at end of file diff --git a/compiler/errors.h b/compiler/errors.h index c42e557..fa67470 100644 --- a/compiler/errors.h +++ b/compiler/errors.h @@ -2,43 +2,43 @@ #define BASIL_ERRORS_H #include "util/defs.h" -#include "util/str.h" #include "util/io.h" #include "util/slice.h" +#include "util/str.h" namespace basil { - class Token; - class Source; - - struct SourceLocation { - u32 line; - u16 column, length; - - SourceLocation(); - SourceLocation(u32 line_in, u16 column_in, u16 length_in = 1); - SourceLocation(const Token& token); - // expand later with other types of objects - }; - - // has length of zero, indicates position not in source - extern const SourceLocation NO_LOCATION; - - void err(SourceLocation loc, const string& message); - u32 error_count(); - void clear_errors(); - void print_errors(stream& io); - void print_errors(stream& io, const Source& src); - void silence_errors(); - void unsilence_errors(); - - template - void err(SourceLocation loc, Args... args) { - buffer b; - write(b, args...); - string message; - while (b.peek()) message += b.read(); - basil::err(loc, message); - } -} + class Token; + class Source; + + struct SourceLocation { + u32 line; + u16 column, length; + + SourceLocation(); + SourceLocation(u32 line_in, u16 column_in, u16 length_in = 1); + SourceLocation(const Token& token); + // expand later with other types of objects + }; + + // has length of zero, indicates position not in source + extern const SourceLocation NO_LOCATION; + + void err(SourceLocation loc, const string& message); + u32 error_count(); + void clear_errors(); + void print_errors(stream& io); + void print_errors(stream& io, const Source& src); + void silence_errors(); + void unsilence_errors(); + + template + void err(SourceLocation loc, Args... args) { + buffer b; + write(b, args...); + string message; + while (b.peek()) message += b.read(); + basil::err(loc, message); + } +} // namespace basil #endif \ No newline at end of file diff --git a/compiler/eval.h b/compiler/eval.h index ca3b4e6..a22928b 100644 --- a/compiler/eval.h +++ b/compiler/eval.h @@ -1,17 +1,17 @@ #ifndef BASIL_EVAL_H #define BASIL_EVAL_H +#include "env.h" #include "util/defs.h" #include "values.h" -#include "env.h" namespace basil { - ref create_root_env(); + ref create_root_env(); - bool introduces_env(const Value& list); + bool introduces_env(const Value& list); - void prep(ref env, Value& term); - Value eval(ref env, Value term); -} + void prep(ref env, Value& term); + Value eval(ref env, Value term); +} // namespace basil #endif \ No newline at end of file diff --git a/compiler/ir.cpp b/compiler/ir.cpp index ab1c152..d936d2e 100644 --- a/compiler/ir.cpp +++ b/compiler/ir.cpp @@ -1,812 +1,755 @@ #include "ir.h" -#include "util/hash.h" #include "ops.h" +#include "util/hash.h" namespace basil { - using namespace x64; - - Location loc_none() { - Location loc; - loc.type = LOC_NONE; - return loc; - } - - Location loc_immediate(i64 i) { - Location loc; - loc.type = LOC_IMMEDIATE; - loc.immediate = i; - return loc; - } - - Location loc_label(const string& label) { - Location loc; - loc.type = LOC_LABEL; - loc.label_index = find_label(label); - return loc; - } - - Insn::Insn(InsnType kind): - _kind(kind), _loc(loc_none()), _func(nullptr) {} - - Insn::~Insn() { - // - } - - InsnType Insn::kind() const { - return _kind; - } - - void Insn::setfunc(Function* func) { - _func = func; - } - - const Location& Insn::loc() const { - return _loc; - } - - Location& Insn::loc() { - if (_loc.type == LOC_NONE) _loc = lazy_loc(); - return _loc; - } - - vector& Insn::succ() { - return _succ; - } - - const vector& Insn::succ() const { - return _succ; - } - - set& Insn::in() { - return _in; - } - - set& Insn::out() { - return _out; - } - - const set& Insn::in() const { - return _in; - } - - const set& Insn::out() const { - return _out; - } - - static u32 anonymous_locals = 0; - static u32 anonymous_labels = 0; - static vector all_labels; - static map label_map; - static vector all_locals; - static vector all_constants; - - u32 find_label(const string& label) { - auto it = label_map.find(label); - if (it == label_map.end()) return add_label(label); - return it->second; - } - - u32 add_label(const string& label) { - all_labels.push(label); - label_map[label] = all_labels.size() - 1; - return all_labels.size() - 1; - } - - u32 next_label() { - buffer b; - write(b, ".L", anonymous_labels ++); - string s; - read(b, s); - all_labels.push(s); - label_map[s] = all_labels.size() - 1; - return all_labels.size() - 1; - } - - Location next_local(const Type* t) { - buffer b; - write(b, ".t", anonymous_locals ++); - string s; - read(b, s); - all_locals.push({ s, 0, t, -1, 0 }); - - Location loc; - loc.type = LOC_LOCAL; - loc.local_index = all_locals.size() - 1; - return loc; - } - - Location const_loc(u32 label, const string& constant) { - ConstantInfo info; - info.type = STRING; - info.name = all_labels[label]; - for (u32 i = 0; i < constant.size(); i ++) info.data.push(constant[i]); - info.data.push('\0'); - - all_constants.push(info); - Location loc; - loc.type = LOC_CONSTANT; - loc.constant_index = all_constants.size() - 1; - return loc; - } - - Symbol symbol_for_label(u32 label, SymbolLinkage type) { - return type == GLOBAL_SYMBOL ? - global((const char*)all_labels[label].raw()) - : local((const char*)all_labels[label].raw()); - } - - void emit_constants(Object& object) { - using namespace x64; - writeto(object); - for (const ConstantInfo& info : all_constants) { - global_label(info.name); - for (u8 b : info.data) object.code().write(b); - } - } - - const Type* ssa_type(const Location& loc) { - switch (loc.type) { - case LOC_NONE: - return VOID; - case LOC_LOCAL: - return all_locals[loc.local_index].type; - case LOC_CONSTANT: - return all_constants[loc.constant_index].type; - case LOC_IMMEDIATE: - return INT; // close enough at this stage - case LOC_LABEL: - return INT; // ...close enough :p - case LOC_REGISTER: - return INT; - } - } - - const u8 BINARY_INSN = 128; // BINARY_INSN kind flag - - i64 immediate_of(const Location& loc) { - return loc.immediate; - } - - const string& label_of(const Location& loc) { - return all_labels[loc.label_index]; - } - - LocalInfo& local_of(const Location& loc) { - return all_locals[loc.local_index]; - } - - ConstantInfo& constant_of(const Location& loc) { - return all_constants[loc.constant_index]; - } - - Location loc_register(u32 reg) { - Location loc; - loc.type = LOC_REGISTER; - loc.reg = reg; - return loc; - } - - void Function::liveness() { - bool changed = true; - while (changed) { - changed = false; - for (i64 i = i64(_insns.size()) - 1; i >= 0; i --) { - Insn* in = _insns[i]; - u32 initial_in = in->in().size(), initial_out = in->out().size(); - for (const Insn* succ : in->succ()) { - for (u32 l : succ->in()) in->out().insert(l); - } - for (u32 l : in->out()) in->in().insert(l); - in->liveout(); - if (in->in().size() != initial_in || in->out().size() != initial_out) - changed = true; - } - } - - // for (i64 i = 0; i < _insns.size(); i ++) { - // print(_insns[i], " { "); - // for (auto u : _insns[i]->in()) print(all_locals[u].name, " "); - // print("} -> { "); - // for (auto u : _insns[i]->out()) print(all_locals[u].name, " "); - // println("}"); - // } - } - - void Function::to_registers() { - map> ranges; - for (i64 i = 0; i < _insns.size(); i ++) { - for (u32 l : _insns[i]->out()) { - if (_insns[i]->in().find(l) == _insns[i]->in().end()) - ranges.put(l, { i, -1 }); - } - for (u32 l : _insns[i]->in()) { - if (_insns[i]->out().find(l) == _insns[i]->out().end()) - ranges[l].second = i; - } - } - - // for (auto& p : ranges) { - // println(all_locals[p.first].name, " live between ", p.second.first, " and ", p.second.second); - // } - - vector> gens, kills; - for (i64 i = 0; i < _insns.size(); i ++) - gens.push({}), kills.push({}); - for (auto& p : ranges) { - gens[p.second.first].push(p.first); - kills[p.second.second].push(p.first); - } - - // for (i64 i = 0; i < _insns.size(); i ++) { - // print(_insns[i], ": "); - // for (u32 l : gens[i]) print("GEN-", all_locals[l].name, " "); - // for (u32 l : kills[i]) print("KILL-", all_locals[l].name, " "); - // println(""); - // } - - vector reg_stack = allocatable_registers(); - for (i64 i = 0; i < _insns.size(); i ++) { - for (u32 g : gens[i]) { - if (all_locals[g].reg >= 0) - ; // - else if (reg_stack.size() == 0) - all_locals[g].offset = -(_stack += 8); // spill - else - all_locals[g].reg = reg_stack.back(), reg_stack.pop(); - } - for (u32 k : kills[i]) { - if (all_locals[k].reg != -1) - reg_stack.push(all_locals[k].reg); - } - } - - for (u32 i = 0; i < all_locals.size(); i ++) { - if (all_locals[i].reg == -1 && all_locals[i].offset == 0) - all_locals[i].reg = RAX; // clobber RAX for dead code (for now) - } - } - - Function::Function(u32 label): - _stack(0), _label(label), _end(next_label()) { - _ret = loc_register(RAX); - } - - Function::Function(const string& label): - Function(add_label(label)) {} - - Location Function::create_local(const Type* t) { - Location l = basil::next_local(t); - _locals.push(l); - return l; - } - - Function::~Function() { - for (Insn* i : _insns) delete i; - for (Function* f : _fns) delete f; - } - - void Function::place_label(u32 label) { - _labels[label] = _insns.size(); - } - - Function& Function::create_function() { - _fns.push(new Function(next_label())); - return *_fns.back(); - } - - Function& Function::create_function(const string& name) { - _fns.push(new Function(name)); - return *_fns.back(); - } - - Location Function::create_local(const string& name, const Type* t) { - LocalInfo info = { name, 0, t, -1, 0 }; - all_locals.push(info); - - Location loc; - loc.type = LOC_LOCAL; - loc.local_index = all_locals.size() - 1; - _locals.push(loc); - return loc; - } - - Location Function::add(Insn* insn) { - insn->setfunc(this); - if (_insns.size() > 0) - _insns.back()->succ().push(insn); - _insns.push(insn); - return insn->loc(); - } - - u32 Function::label() const { - return _label; - } - - void Function::allocate() { - for (Function* fn : _fns) fn->allocate(); - liveness(); - to_registers(); - } - - void Function::emit(Object& obj, bool exit) { - for (Function* fn : _fns) fn->emit(obj); - - writeto(obj); - global_label(all_labels[_label]); - open_frame(_stack); - for (Insn* i : _insns) i->emit(); - local_label(all_labels[_end]); - if (exit && all_labels[_label] == "_start") { - mov(x64::r64(x64::RAX), x64::imm64(60)); - mov(x64::r64(x64::RDI), x64::imm64(0)); - syscall(); - } - close_frame(_stack); - } - - void Function::format(stream& io) const { - for (Function* fn : _fns) fn->format(io); - writeln(io, all_labels[_label], ":"); - for (Insn* i : _insns) writeln(io, " ", i); - } - - u32 Function::end_label() const { - return _end; - } - - const Location& Function::ret_loc() const { - return _ret; - } - - Insn* Function::last() const { - if (_insns.size() == 0) return nullptr; - return _insns.back(); - } - - Location LoadInsn::lazy_loc() { - return _func->create_local(ssa_type(_src)); - } - - LoadInsn::LoadInsn(Location src): - Insn(LOAD_INSN), _src(src) {} - - void LoadInsn::emit() { - move(_loc, _src); - } - - void LoadInsn::format(stream& io) const { - write(io, _loc, " = ", _src); - } - - void LoadInsn::liveout() { - if (_src.type == LOC_LOCAL) in().insert(_src.local_index); - if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); - } - - Location StoreInsn::lazy_loc() { - return _dest; - } - - StoreInsn::StoreInsn(Location dest, Location src): - Insn(STORE_INSN), _dest(dest), _src(src) {} - - void StoreInsn::emit() { - move(_dest, _src); - } - - void StoreInsn::format(stream& io) const { - write(io, _loc, " = ", _src); - } - - void StoreInsn::liveout() { - if (_src.type == LOC_LOCAL) in().insert(_src.local_index); - if (_dest.type == LOC_LOCAL) in().erase(_dest.local_index); - } - - LoadPtrInsn::LoadPtrInsn(Location src, const Type* t, i32 offset): - Insn(LOAD_PTR_INSN), _src(src), _type(t), _offset(offset) {} - - Location LoadPtrInsn::lazy_loc() { - return _func->create_local(_type); - } - - void LoadPtrInsn::emit() { - load(_loc, _src, _offset); - } - - void LoadPtrInsn::format(stream& io) const { - write(io, _loc, " = *", _src); - } - - void LoadPtrInsn::liveout() { - if (_src.type == LOC_LOCAL) in().insert(_src.local_index); - if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); - } - - StorePtrInsn::StorePtrInsn(Location dest, Location src, i32 offset): - Insn(STORE_PTR_INSN), _dest(dest), _src(src), _offset(offset) {} - - Location StorePtrInsn::lazy_loc() { - return loc_none(); - } - - void StorePtrInsn::emit() { - store(_dest, _src, _offset); - } - - void StorePtrInsn::format(stream& io) const { - write(io, "*", _dest, " = ", _src); - } - - void StorePtrInsn::liveout() { - if (_src.type == LOC_LOCAL) in().insert(_src.local_index); - if (_dest.type == LOC_LOCAL) in().insert(_dest.local_index); - } - - AddressInsn::AddressInsn(Location src, const Type* t): - Insn(ADDRESS_INSN), _src(src), _type(t) {} - - Location AddressInsn::lazy_loc() { - return _func->create_local(_type); - } - - void AddressInsn::emit() { - lea(_loc, _src); - } - - void AddressInsn::format(stream& io) const { - write(io, _loc, " = &", _src); - } + using namespace x64; + + Location loc_none() { + Location loc; + loc.type = LOC_NONE; + return loc; + } + + Location loc_immediate(i64 i) { + Location loc; + loc.type = LOC_IMMEDIATE; + loc.immediate = i; + return loc; + } + + Location loc_label(const string& label) { + Location loc; + loc.type = LOC_LABEL; + loc.label_index = find_label(label); + return loc; + } + + Insn::Insn(InsnType kind) : _kind(kind), _loc(loc_none()), _func(nullptr) {} + + Insn::~Insn() { + // + } + + InsnType Insn::kind() const { + return _kind; + } + + void Insn::setfunc(Function* func) { + _func = func; + } + + const Location& Insn::loc() const { + return _loc; + } + + Location& Insn::loc() { + if (_loc.type == LOC_NONE) _loc = lazy_loc(); + return _loc; + } + + vector& Insn::succ() { + return _succ; + } + + const vector& Insn::succ() const { + return _succ; + } + + set& Insn::in() { + return _in; + } + + set& Insn::out() { + return _out; + } + + const set& Insn::in() const { + return _in; + } + + const set& Insn::out() const { + return _out; + } + + static u32 anonymous_locals = 0; + static u32 anonymous_labels = 0; + static vector all_labels; + static map label_map; + static vector all_locals; + static vector all_constants; + + u32 find_label(const string& label) { + auto it = label_map.find(label); + if (it == label_map.end()) return add_label(label); + return it->second; + } + + u32 add_label(const string& label) { + all_labels.push(label); + label_map[label] = all_labels.size() - 1; + return all_labels.size() - 1; + } + + u32 next_label() { + buffer b; + write(b, ".L", anonymous_labels++); + string s; + read(b, s); + all_labels.push(s); + label_map[s] = all_labels.size() - 1; + return all_labels.size() - 1; + } + + Location next_local(const Type* t) { + buffer b; + write(b, ".t", anonymous_locals++); + string s; + read(b, s); + all_locals.push({s, 0, t, -1, 0}); + + Location loc; + loc.type = LOC_LOCAL; + loc.local_index = all_locals.size() - 1; + return loc; + } + + Location const_loc(u32 label, const string& constant) { + ConstantInfo info; + info.type = STRING; + info.name = all_labels[label]; + for (u32 i = 0; i < constant.size(); i++) info.data.push(constant[i]); + info.data.push('\0'); + + all_constants.push(info); + Location loc; + loc.type = LOC_CONSTANT; + loc.constant_index = all_constants.size() - 1; + return loc; + } + + Symbol symbol_for_label(u32 label, SymbolLinkage type) { + return type == GLOBAL_SYMBOL ? global((const char*)all_labels[label].raw()) + : local((const char*)all_labels[label].raw()); + } + + void emit_constants(Object& object) { + using namespace x64; + writeto(object); + for (const ConstantInfo& info : all_constants) { + global_label(info.name); + for (u8 b : info.data) object.code().write(b); + } + } + + const Type* ssa_type(const Location& loc) { + switch (loc.type) { + case LOC_NONE: return VOID; + case LOC_LOCAL: return all_locals[loc.local_index].type; + case LOC_CONSTANT: return all_constants[loc.constant_index].type; + case LOC_IMMEDIATE: return INT; // close enough at this stage + case LOC_LABEL: return INT; // ...close enough :p + case LOC_REGISTER: return INT; + } + } + + const u8 BINARY_INSN = 128; // BINARY_INSN kind flag + + i64 immediate_of(const Location& loc) { + return loc.immediate; + } + + const string& label_of(const Location& loc) { + return all_labels[loc.label_index]; + } + + LocalInfo& local_of(const Location& loc) { + return all_locals[loc.local_index]; + } + + ConstantInfo& constant_of(const Location& loc) { + return all_constants[loc.constant_index]; + } + + Location loc_register(u32 reg) { + Location loc; + loc.type = LOC_REGISTER; + loc.reg = reg; + return loc; + } + + void Function::liveness() { + bool changed = true; + while (changed) { + changed = false; + for (i64 i = i64(_insns.size()) - 1; i >= 0; i--) { + Insn* in = _insns[i]; + u32 initial_in = in->in().size(), initial_out = in->out().size(); + for (const Insn* succ : in->succ()) { + for (u32 l : succ->in()) in->out().insert(l); + } + for (u32 l : in->out()) in->in().insert(l); + in->liveout(); + if (in->in().size() != initial_in || in->out().size() != initial_out) changed = true; + } + } + + // for (i64 i = 0; i < _insns.size(); i ++) { + // print(_insns[i], " { "); + // for (auto u : _insns[i]->in()) print(all_locals[u].name, " "); + // print("} -> { "); + // for (auto u : _insns[i]->out()) print(all_locals[u].name, " "); + // println("}"); + // } + } + + void Function::to_registers() { + map> ranges; + for (i64 i = 0; i < _insns.size(); i++) { + for (u32 l : _insns[i]->out()) { + if (_insns[i]->in().find(l) == _insns[i]->in().end()) ranges.put(l, {i, -1}); + } + for (u32 l : _insns[i]->in()) { + if (_insns[i]->out().find(l) == _insns[i]->out().end()) ranges[l].second = i; + } + } + + // for (auto& p : ranges) { + // println(all_locals[p.first].name, " live between ", p.second.first, " and ", p.second.second); + // } + + vector> gens, kills; + for (i64 i = 0; i < _insns.size(); i++) gens.push({}), kills.push({}); + for (auto& p : ranges) { + gens[p.second.first].push(p.first); + kills[p.second.second].push(p.first); + } + + // for (i64 i = 0; i < _insns.size(); i ++) { + // print(_insns[i], ": "); + // for (u32 l : gens[i]) print("GEN-", all_locals[l].name, " "); + // for (u32 l : kills[i]) print("KILL-", all_locals[l].name, " "); + // println(""); + // } + + vector reg_stack = allocatable_registers(); + for (i64 i = 0; i < _insns.size(); i++) { + for (u32 g : gens[i]) { + if (all_locals[g].reg >= 0) + ; // + else if (reg_stack.size() == 0) + all_locals[g].offset = -(_stack += 8); // spill + else + all_locals[g].reg = reg_stack.back(), reg_stack.pop(); + } + for (u32 k : kills[i]) { + if (all_locals[k].reg != -1) reg_stack.push(all_locals[k].reg); + } + } + + for (u32 i = 0; i < all_locals.size(); i++) { + if (all_locals[i].reg == -1 && all_locals[i].offset == 0) + all_locals[i].reg = RAX; // clobber RAX for dead code (for now) + } + } + + Function::Function(u32 label) : _stack(0), _label(label), _end(next_label()) { + _ret = loc_register(RAX); + } + + Function::Function(const string& label) : Function(add_label(label)) {} + + Location Function::create_local(const Type* t) { + Location l = basil::next_local(t); + _locals.push(l); + return l; + } + + Function::~Function() { + for (Insn* i : _insns) delete i; + for (Function* f : _fns) delete f; + } + + void Function::place_label(u32 label) { + _labels[label] = _insns.size(); + } + + Function& Function::create_function() { + _fns.push(new Function(next_label())); + return *_fns.back(); + } + + Function& Function::create_function(const string& name) { + _fns.push(new Function(name)); + return *_fns.back(); + } + + Location Function::create_local(const string& name, const Type* t) { + LocalInfo info = {name, 0, t, -1, 0}; + all_locals.push(info); + + Location loc; + loc.type = LOC_LOCAL; + loc.local_index = all_locals.size() - 1; + _locals.push(loc); + return loc; + } + + Location Function::add(Insn* insn) { + insn->setfunc(this); + if (_insns.size() > 0) _insns.back()->succ().push(insn); + _insns.push(insn); + return insn->loc(); + } + + u32 Function::label() const { + return _label; + } + + void Function::allocate() { + for (Function* fn : _fns) fn->allocate(); + liveness(); + to_registers(); + } + + void Function::emit(Object& obj, bool exit) { + for (Function* fn : _fns) fn->emit(obj); + + writeto(obj); + global_label(all_labels[_label]); + open_frame(_stack); + for (Insn* i : _insns) i->emit(); + local_label(all_labels[_end]); + if (exit && all_labels[_label] == "_start") { + mov(x64::r64(x64::RAX), x64::imm64(60)); + mov(x64::r64(x64::RDI), x64::imm64(0)); + syscall(); + } + close_frame(_stack); + } + + void Function::format(stream& io) const { + for (Function* fn : _fns) fn->format(io); + writeln(io, all_labels[_label], ":"); + for (Insn* i : _insns) writeln(io, " ", i); + } + + u32 Function::end_label() const { + return _end; + } + + const Location& Function::ret_loc() const { + return _ret; + } + + Insn* Function::last() const { + if (_insns.size() == 0) return nullptr; + return _insns.back(); + } + + Location LoadInsn::lazy_loc() { + return _func->create_local(ssa_type(_src)); + } + + LoadInsn::LoadInsn(Location src) : Insn(LOAD_INSN), _src(src) {} + + void LoadInsn::emit() { + move(_loc, _src); + } + + void LoadInsn::format(stream& io) const { + write(io, _loc, " = ", _src); + } + + void LoadInsn::liveout() { + if (_src.type == LOC_LOCAL) in().insert(_src.local_index); + if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); + } + + Location StoreInsn::lazy_loc() { + return _dest; + } + + StoreInsn::StoreInsn(Location dest, Location src) : Insn(STORE_INSN), _dest(dest), _src(src) {} + + void StoreInsn::emit() { + move(_dest, _src); + } + + void StoreInsn::format(stream& io) const { + write(io, _loc, " = ", _src); + } + + void StoreInsn::liveout() { + if (_src.type == LOC_LOCAL) in().insert(_src.local_index); + if (_dest.type == LOC_LOCAL) in().erase(_dest.local_index); + } + + LoadPtrInsn::LoadPtrInsn(Location src, const Type* t, i32 offset) + : Insn(LOAD_PTR_INSN), _src(src), _type(t), _offset(offset) {} + + Location LoadPtrInsn::lazy_loc() { + return _func->create_local(_type); + } + + void LoadPtrInsn::emit() { + load(_loc, _src, _offset); + } + + void LoadPtrInsn::format(stream& io) const { + write(io, _loc, " = *", _src); + } - void AddressInsn::liveout() { - if (_src.type == LOC_LOCAL) in().insert(_src.local_index); - if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); - } + void LoadPtrInsn::liveout() { + if (_src.type == LOC_LOCAL) in().insert(_src.local_index); + if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); + } - BinaryInsn::BinaryInsn(InsnType kind, const char* name, Location left, - Location right): - Insn(kind), _name(name), _left(left), _right(right) {} + StorePtrInsn::StorePtrInsn(Location dest, Location src, i32 offset) + : Insn(STORE_PTR_INSN), _dest(dest), _src(src), _offset(offset) {} - void BinaryInsn::format(stream& io) const { - write(io, _loc, " = ", _left, " ", _name, " ", _right); - } + Location StorePtrInsn::lazy_loc() { + return loc_none(); + } - void BinaryInsn::liveout() { - if (_left.type == LOC_LOCAL) in().insert(_left.local_index); - if (_right.type == LOC_LOCAL) in().insert(_right.local_index); - if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); - } - - Location BinaryMathInsn::lazy_loc() { - return _func->create_local(ssa_type(_left)); - } + void StorePtrInsn::emit() { + store(_dest, _src, _offset); + } - BinaryMathInsn::BinaryMathInsn(InsnType kind, const char* name, Location left, - Location right): - BinaryInsn(kind, name, left, right) {} + void StorePtrInsn::format(stream& io) const { + write(io, "*", _dest, " = ", _src); + } - AddInsn::AddInsn(Location left, Location right): - BinaryMathInsn(ADD_INSN, "+", left, right) {} + void StorePtrInsn::liveout() { + if (_src.type == LOC_LOCAL) in().insert(_src.local_index); + if (_dest.type == LOC_LOCAL) in().insert(_dest.local_index); + } - void AddInsn::emit() { - add(_loc, _left, _right); - } + AddressInsn::AddressInsn(Location src, const Type* t) : Insn(ADDRESS_INSN), _src(src), _type(t) {} - SubInsn::SubInsn(Location left, Location right): - BinaryMathInsn(SUB_INSN, "-", left, right) {} + Location AddressInsn::lazy_loc() { + return _func->create_local(_type); + } - void SubInsn::emit() { - sub(_loc, _left, _right); - } + void AddressInsn::emit() { + lea(_loc, _src); + } - MulInsn::MulInsn(Location left, Location right): - BinaryMathInsn(MUL_INSN, "*", left, right) {} + void AddressInsn::format(stream& io) const { + write(io, _loc, " = &", _src); + } - void MulInsn::emit() { - mul(_loc, _left, _right); - } + void AddressInsn::liveout() { + if (_src.type == LOC_LOCAL) in().insert(_src.local_index); + if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); + } - DivInsn::DivInsn(Location left, Location right): - BinaryMathInsn(DIV_INSN, "/", left, right) {} + BinaryInsn::BinaryInsn(InsnType kind, const char* name, Location left, Location right) + : Insn(kind), _name(name), _left(left), _right(right) {} - void DivInsn::emit() { - div(_loc, _left, _right); - } + void BinaryInsn::format(stream& io) const { + write(io, _loc, " = ", _left, " ", _name, " ", _right); + } - RemInsn::RemInsn(Location left, Location right): - BinaryMathInsn(REM_INSN, "%", left, right) {} + void BinaryInsn::liveout() { + if (_left.type == LOC_LOCAL) in().insert(_left.local_index); + if (_right.type == LOC_LOCAL) in().insert(_right.local_index); + if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); + } - void RemInsn::emit() { - rem(_loc, _left, _right); - } + Location BinaryMathInsn::lazy_loc() { + return _func->create_local(ssa_type(_left)); + } - BinaryLogicInsn::BinaryLogicInsn(InsnType kind, const char* name, Location left, - Location right): - BinaryInsn(kind, name, left, right) {} + BinaryMathInsn::BinaryMathInsn(InsnType kind, const char* name, Location left, Location right) + : BinaryInsn(kind, name, left, right) {} - Location BinaryLogicInsn::lazy_loc() { - return _func->create_local(BOOL); - } + AddInsn::AddInsn(Location left, Location right) : BinaryMathInsn(ADD_INSN, "+", left, right) {} - AndInsn::AndInsn(Location left, Location right): - BinaryLogicInsn(AND_INSN, "and", left, right) {} + void AddInsn::emit() { + add(_loc, _left, _right); + } - void AndInsn::emit() { - and_op(_loc, _left, _right); - } + SubInsn::SubInsn(Location left, Location right) : BinaryMathInsn(SUB_INSN, "-", left, right) {} - OrInsn::OrInsn(Location left, Location right): - BinaryLogicInsn(OR_INSN, "or", left, right) {} + void SubInsn::emit() { + sub(_loc, _left, _right); + } - void OrInsn::emit() { - or_op(_loc, _left, _right); - } + MulInsn::MulInsn(Location left, Location right) : BinaryMathInsn(MUL_INSN, "*", left, right) {} - XorInsn::XorInsn(Location left, Location right): - BinaryLogicInsn(XOR_INSN, "xor", left, right) {} + void MulInsn::emit() { + mul(_loc, _left, _right); + } - void XorInsn::emit() { - xor_op(_loc, _left, _right); - } + DivInsn::DivInsn(Location left, Location right) : BinaryMathInsn(DIV_INSN, "/", left, right) {} - NotInsn::NotInsn(Location src): - Insn(NOT_INSN), _src(src) {} + void DivInsn::emit() { + div(_loc, _left, _right); + } - Location NotInsn::lazy_loc() { - return _func->create_local(BOOL); - } + RemInsn::RemInsn(Location left, Location right) : BinaryMathInsn(REM_INSN, "%", left, right) {} - void NotInsn::emit() { - not_op(_loc, _src); - } + void RemInsn::emit() { + rem(_loc, _left, _right); + } - void NotInsn::format(stream& io) const { - write(io, _loc, " = ", "not ", _src); - } + BinaryLogicInsn::BinaryLogicInsn(InsnType kind, const char* name, Location left, Location right) + : BinaryInsn(kind, name, left, right) {} - void NotInsn::liveout() { - if (_src.type == LOC_LOCAL) in().insert(_src.local_index); - if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); - } + Location BinaryLogicInsn::lazy_loc() { + return _func->create_local(BOOL); + } - BinaryEqualityInsn::BinaryEqualityInsn(InsnType kind, const char* name, - Location left, Location right): - BinaryInsn(kind, name, left, right) {} + AndInsn::AndInsn(Location left, Location right) : BinaryLogicInsn(AND_INSN, "and", left, right) {} - Location BinaryEqualityInsn::lazy_loc() { - return _func->create_local(BOOL); - } + void AndInsn::emit() { + and_op(_loc, _left, _right); + } - EqualInsn::EqualInsn(Location left, Location right): - BinaryEqualityInsn(EQ_INSN, "==", left, right) {} + OrInsn::OrInsn(Location left, Location right) : BinaryLogicInsn(OR_INSN, "or", left, right) {} - void EqualInsn::emit() { - equal(_loc, _left, _right); - } + void OrInsn::emit() { + or_op(_loc, _left, _right); + } - InequalInsn::InequalInsn(Location left, Location right): - BinaryEqualityInsn(NOT_EQ_INSN, "!=", left, right) {} + XorInsn::XorInsn(Location left, Location right) : BinaryLogicInsn(XOR_INSN, "xor", left, right) {} - void InequalInsn::emit() { - not_equal(_loc, _left, _right); - } + void XorInsn::emit() { + xor_op(_loc, _left, _right); + } - BinaryRelationInsn::BinaryRelationInsn(InsnType kind, const char* name, - Location left, Location right): - BinaryInsn(kind, name, left, right) {} - - Location BinaryRelationInsn::lazy_loc() { - return _func->create_local(BOOL); - } - - LessInsn::LessInsn(Location left, Location right): - BinaryRelationInsn(LESS_INSN, "<", left, right) {} - - void LessInsn::emit() { - less(_loc, _left, _right); - } - - LessEqualInsn::LessEqualInsn(Location left, Location right): - BinaryRelationInsn(LESS_EQ_INSN, "<=", left, right) {} - - void LessEqualInsn::emit() { - less_equal(_loc, _left, _right); - } - - GreaterInsn::GreaterInsn(Location left, Location right): - BinaryRelationInsn(GREATER_INSN, ">", left, right) {} - - void GreaterInsn::emit() { - greater(_loc, _left, _right); - } - - GreaterEqualInsn::GreaterEqualInsn(Location left, Location right): - BinaryRelationInsn(GREATER_EQ_INSN, ">=", left, right) {} - - void GreaterEqualInsn::emit() { - greater_equal(_loc, _left, _right); - } - - Location RetInsn::lazy_loc() { - return loc_none(); - } - - RetInsn::RetInsn(Location src): - Insn(RET_INSN), _src(src) {} - - void RetInsn::emit() { - move(_func->ret_loc(), _src); - if (this != _func->last()) { - Location loc; - loc.type = LOC_LABEL; - loc.label_index = _func->end_label(); - jump(loc); - } - } - - void RetInsn::format(stream& io) const { - write(io, "return ", _src); - } - - void RetInsn::liveout() { - while (in().size()) in().erase(*in().begin()); // all other variables die here - while (out().size()) out().erase(*out().begin()); // all other variables die here - if (_src.type == LOC_LOCAL) - in().insert(_src.local_index); - } - - LoadArgumentInsn::LoadArgumentInsn(u32 index, const Type* type): - Insn(LOAD_ARG_INSN), _index(index), _type(type) {} - - Location LoadArgumentInsn::lazy_loc() { - Location l = _func->create_local(_type); - return l; - } - - void LoadArgumentInsn::emit() { - get_arg(_loc, _index); - } - - void LoadArgumentInsn::format(stream& io) const { - write(io, _loc, " = $", _index); - } - - void LoadArgumentInsn::liveout() { - if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); - } - - CallInsn::CallInsn(Location fn, const vector& args, const Type* ret): - Insn(CALL_INSN), _fn(fn), _args(args), _ret(ret) {} - - Location CallInsn::lazy_loc() { - return _func->create_local(_ret); - } - - void CallInsn::emit() { - vector saved; - for (u32 i : in()) { - if (out().find(i) != out().end() && all_locals[i].reg >= 0) { - Location loc; - loc.type = LOC_LOCAL; - loc.local_index = i; - saved.push(loc); - } - } - for (u32 i = 0; i < saved.size(); i ++) push(saved[i]); - for (u32 i = 0; i < _args.size(); i ++) - set_arg(i, _args[i]); - call(_loc, _fn); - for (i64 i = i64(saved.size()) - 1; i >= 0; i --) pop(saved[i]); - } - - void CallInsn::format(stream& io) const { - write(io, _loc, " = ", _fn, "()"); - } - - void CallInsn::liveout() { - for (const Location& loc : _args) if (loc.type == LOC_LOCAL) - in().insert(loc.local_index); - if (_fn.type == LOC_LOCAL) in().insert(_fn.local_index); - if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); - } - - Label::Label(u32 label): - Insn(LABEL), _label(label) {} - - Location Label::lazy_loc() { - return loc_none(); - } - - void Label::emit() { - local_label(all_labels[_label]); - } - - void Label::format(stream& io) const { - write(io, "\b\b\b\b", all_labels[_label], ":"); - } - - void Label::liveout() { - // - } - - GotoInsn::GotoInsn(u32 label): - Insn(GOTO_INSN), _label(label) {} - - Location GotoInsn::lazy_loc() { - return loc_none(); - } - - void GotoInsn::emit() { - jmp(label64(symbol_for_label(_label, LOCAL_SYMBOL))); - } - - void GotoInsn::format(stream& io) const { - write(io, "goto ", all_labels[_label]); - } - - void GotoInsn::liveout() { - // - } - - IfZeroInsn::IfZeroInsn(u32 label, Location cond): - Insn(IFZERO_INSN), _label(label), _cond(cond) {} - - Location IfZeroInsn::lazy_loc() { - return loc_none(); - } - - void IfZeroInsn::emit() { - Location label; - label.type = LOC_LABEL; - label.label_index = _label; - jump_if_zero(label, _cond); - } - - void IfZeroInsn::format(stream& io) const { - write(io, "if not ", _cond, " goto ", all_labels[_label]); - } - - void IfZeroInsn::liveout() { - if (_cond.type == LOC_LOCAL) in().insert(_cond.local_index); - } -} + NotInsn::NotInsn(Location src) : Insn(NOT_INSN), _src(src) {} + + Location NotInsn::lazy_loc() { + return _func->create_local(BOOL); + } + + void NotInsn::emit() { + not_op(_loc, _src); + } + + void NotInsn::format(stream& io) const { + write(io, _loc, " = ", "not ", _src); + } + + void NotInsn::liveout() { + if (_src.type == LOC_LOCAL) in().insert(_src.local_index); + if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); + } + + BinaryEqualityInsn::BinaryEqualityInsn(InsnType kind, const char* name, Location left, Location right) + : BinaryInsn(kind, name, left, right) {} + + Location BinaryEqualityInsn::lazy_loc() { + return _func->create_local(BOOL); + } + + EqualInsn::EqualInsn(Location left, Location right) : BinaryEqualityInsn(EQ_INSN, "==", left, right) {} + + void EqualInsn::emit() { + equal(_loc, _left, _right); + } + + InequalInsn::InequalInsn(Location left, Location right) : BinaryEqualityInsn(NOT_EQ_INSN, "!=", left, right) {} + + void InequalInsn::emit() { + not_equal(_loc, _left, _right); + } + + BinaryRelationInsn::BinaryRelationInsn(InsnType kind, const char* name, Location left, Location right) + : BinaryInsn(kind, name, left, right) {} + + Location BinaryRelationInsn::lazy_loc() { + return _func->create_local(BOOL); + } + + LessInsn::LessInsn(Location left, Location right) : BinaryRelationInsn(LESS_INSN, "<", left, right) {} + + void LessInsn::emit() { + less(_loc, _left, _right); + } + + LessEqualInsn::LessEqualInsn(Location left, Location right) : BinaryRelationInsn(LESS_EQ_INSN, "<=", left, right) {} + + void LessEqualInsn::emit() { + less_equal(_loc, _left, _right); + } + + GreaterInsn::GreaterInsn(Location left, Location right) : BinaryRelationInsn(GREATER_INSN, ">", left, right) {} + + void GreaterInsn::emit() { + greater(_loc, _left, _right); + } + + GreaterEqualInsn::GreaterEqualInsn(Location left, Location right) + : BinaryRelationInsn(GREATER_EQ_INSN, ">=", left, right) {} + + void GreaterEqualInsn::emit() { + greater_equal(_loc, _left, _right); + } + + Location RetInsn::lazy_loc() { + return loc_none(); + } + + RetInsn::RetInsn(Location src) : Insn(RET_INSN), _src(src) {} + + void RetInsn::emit() { + move(_func->ret_loc(), _src); + if (this != _func->last()) { + Location loc; + loc.type = LOC_LABEL; + loc.label_index = _func->end_label(); + jump(loc); + } + } + + void RetInsn::format(stream& io) const { + write(io, "return ", _src); + } + + void RetInsn::liveout() { + while (in().size()) in().erase(*in().begin()); // all other variables die here + while (out().size()) out().erase(*out().begin()); // all other variables die here + if (_src.type == LOC_LOCAL) in().insert(_src.local_index); + } + + LoadArgumentInsn::LoadArgumentInsn(u32 index, const Type* type) : Insn(LOAD_ARG_INSN), _index(index), _type(type) {} + + Location LoadArgumentInsn::lazy_loc() { + Location l = _func->create_local(_type); + return l; + } + + void LoadArgumentInsn::emit() { + get_arg(_loc, _index); + } + + void LoadArgumentInsn::format(stream& io) const { + write(io, _loc, " = $", _index); + } + + void LoadArgumentInsn::liveout() { + if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); + } + + CallInsn::CallInsn(Location fn, const vector& args, const Type* ret) + : Insn(CALL_INSN), _fn(fn), _args(args), _ret(ret) {} + + Location CallInsn::lazy_loc() { + return _func->create_local(_ret); + } + + void CallInsn::emit() { + vector saved; + for (u32 i : in()) { + if (out().find(i) != out().end() && all_locals[i].reg >= 0) { + Location loc; + loc.type = LOC_LOCAL; + loc.local_index = i; + saved.push(loc); + } + } + for (u32 i = 0; i < saved.size(); i++) push(saved[i]); + for (u32 i = 0; i < _args.size(); i++) set_arg(i, _args[i]); + call(_loc, _fn); + for (i64 i = i64(saved.size()) - 1; i >= 0; i--) pop(saved[i]); + } + + void CallInsn::format(stream& io) const { + write(io, _loc, " = ", _fn, "()"); + } + + void CallInsn::liveout() { + for (const Location& loc : _args) + if (loc.type == LOC_LOCAL) in().insert(loc.local_index); + if (_fn.type == LOC_LOCAL) in().insert(_fn.local_index); + if (_loc.type == LOC_LOCAL) in().erase(_loc.local_index); + } + + Label::Label(u32 label) : Insn(LABEL), _label(label) {} + + Location Label::lazy_loc() { + return loc_none(); + } + + void Label::emit() { + local_label(all_labels[_label]); + } + + void Label::format(stream& io) const { + write(io, "\b\b\b\b", all_labels[_label], ":"); + } + + void Label::liveout() { + // + } + + GotoInsn::GotoInsn(u32 label) : Insn(GOTO_INSN), _label(label) {} + + Location GotoInsn::lazy_loc() { + return loc_none(); + } + + void GotoInsn::emit() { + jmp(label64(symbol_for_label(_label, LOCAL_SYMBOL))); + } + + void GotoInsn::format(stream& io) const { + write(io, "goto ", all_labels[_label]); + } + + void GotoInsn::liveout() { + // + } + + IfZeroInsn::IfZeroInsn(u32 label, Location cond) : Insn(IFZERO_INSN), _label(label), _cond(cond) {} + + Location IfZeroInsn::lazy_loc() { + return loc_none(); + } + + void IfZeroInsn::emit() { + Location label; + label.type = LOC_LABEL; + label.label_index = _label; + jump_if_zero(label, _cond); + } + + void IfZeroInsn::format(stream& io) const { + write(io, "if not ", _cond, " goto ", all_labels[_label]); + } + + void IfZeroInsn::liveout() { + if (_cond.type == LOC_LOCAL) in().insert(_cond.local_index); + } +} // namespace basil void write(stream& io, const basil::Location& loc) { - switch (loc.type) { - case basil::LOC_NONE: - write(io, "none"); - return; - case basil::LOC_LOCAL: - write(io, basil::all_locals[loc.local_index].name); - if (basil::all_locals[loc.local_index].index > 0 - || basil::all_locals[loc.local_index].name[0] != '.') - write(io, ".", basil::all_locals[loc.local_index].index); - return; - case basil::LOC_IMMEDIATE: - write(io, loc.immediate); - return; - case basil::LOC_LABEL: - write(io, basil::all_labels[loc.label_index]); - return; - case basil::LOC_CONSTANT: - write(io, basil::all_constants[loc.constant_index].name); - return; - case basil::LOC_REGISTER: - write(io, "r", loc.reg); - return; - default: - return; - } + switch (loc.type) { + case basil::LOC_NONE: write(io, "none"); return; + case basil::LOC_LOCAL: + write(io, basil::all_locals[loc.local_index].name); + if (basil::all_locals[loc.local_index].index > 0 || basil::all_locals[loc.local_index].name[0] != '.') + write(io, ".", basil::all_locals[loc.local_index].index); + return; + case basil::LOC_IMMEDIATE: write(io, loc.immediate); return; + case basil::LOC_LABEL: write(io, basil::all_labels[loc.label_index]); return; + case basil::LOC_CONSTANT: write(io, basil::all_constants[loc.constant_index].name); return; + case basil::LOC_REGISTER: write(io, "r", loc.reg); return; + default: return; + } } void write(stream& io, basil::Insn* insn) { - insn->format(io); + insn->format(io); } void write(stream& io, const basil::Insn* insn) { - insn->format(io); + insn->format(io); } void write(stream& io, const basil::Function& func) { - func.format(io); + func.format(io); } \ No newline at end of file diff --git a/compiler/ir.h b/compiler/ir.h index 4ca8c5c..18ae35b 100644 --- a/compiler/ir.h +++ b/compiler/ir.h @@ -1,447 +1,468 @@ #ifndef BASIL_IR_H #define BASIL_IR_H +#include "type.h" #include "util/defs.h" +#include "util/io.h" #include "util/str.h" #include "util/vec.h" -#include "util/io.h" -#include "type.h" #include "jasmine/x64.h" namespace basil { - using namespace jasmine; - - enum LocationType { - LOC_NONE, - LOC_LOCAL, - LOC_IMMEDIATE, - LOC_CONSTANT, - LOC_LABEL, - LOC_REGISTER - }; - - struct LocalInfo { - string name; - u32 index; - const Type* type; - i64 reg; - i64 offset; - }; - - struct ConstantInfo { - string name; - vector data; - const Type* type; - }; - - struct Location { - union { - u32 local_index; - i64 immediate; - u32 constant_index; - u32 label_index; - u32 reg; - }; - LocationType type; - - const Type* value_type(); - }; - - extern const u8 BINARY_INSN; - - enum InsnType { - LOAD_INSN = 0, - STORE_INSN = 1, - LOAD_ARG_INSN = 2, - GOTO_INSN = 3, - IFZERO_INSN = 4, - CALL_INSN = 5, - ADDRESS_INSN = 6, - NOT_INSN = 7, - LOAD_PTR_INSN = 8, - STORE_PTR_INSN = 9, - RET_INSN = 10, - LABEL = 11, - ADD_INSN = 128, - SUB_INSN = 129, - MUL_INSN = 130, - DIV_INSN = 131, - REM_INSN = 132, - AND_INSN = 133, - OR_INSN = 134, - XOR_INSN = 135, - EQ_INSN = 136, - NOT_EQ_INSN = 137, - LESS_INSN = 138, - LESS_EQ_INSN = 139, - GREATER_INSN = 140, - GREATER_EQ_INSN = 141, - }; - - Location loc_none(); - Location loc_immediate(i64 i); - Location loc_label(const string& label); - const Type* type_of(const Location& loc); - - i64 immediate_of(const Location& loc); - const string& label_of(const Location& loc); - LocalInfo& local_of(const Location& loc); - ConstantInfo& constant_of(const Location& loc); - - class Function; - - class Insn { - protected: - InsnType _kind; - Location _loc; - Function* _func; - vector _succ; - set _in, _out; - virtual Location lazy_loc() = 0; - - friend class Function; - void setfunc(Function* func); - public: - Insn(InsnType kind); - virtual ~Insn(); - - InsnType kind() const; - const Location& loc() const; - Location& loc(); - virtual void emit() = 0; - virtual void format(stream& io) const = 0; - vector& succ(); - const vector& succ() const; - set& in(); - set& out(); - const set& in() const; - const set& out() const; - virtual void liveout() = 0; - }; - - u32 find_label(const string& label); - u32 add_label(const string& label); - u32 next_label(); - Location next_local(const Type* t); - Location const_loc(u32 label, const string& constant); - void emit_constants(Object& object); - - class Function { - vector _fns; - vector _insns; - i64 _stack; - vector _locals; - map _labels; - u32 _label; - u32 _end; - Location _ret; - void liveness(); - void to_registers(); - Function(u32 label); - public: - Function(const string& label); - ~Function(); - - void place_label(u32 label); - Function& create_function(); - Function& create_function(const string& name); - Location create_local(const Type* t); - Location create_local(const string& name, const Type* t); - Location add(Insn* insn); - u32 label() const; - void allocate(); - void emit(Object& obj, bool exit = false); - void format(stream& io) const; - u32 end_label() const; - const Location& ret_loc() const; - Insn* last() const; - }; - - class LoadInsn : public Insn { - Location _src; - protected: - Location lazy_loc() override; - public: - LoadInsn(Location src); - - void emit() override; - void format(stream& io) const override; - void liveout() override; - }; - - class StoreInsn : public Insn { - Location _dest, _src; - protected: - Location lazy_loc() override; - public: - StoreInsn(Location dest, Location src); - - void emit() override; - void format(stream& io) const override; - void liveout() override; - }; - - class LoadPtrInsn : public Insn { - Location _src; - const Type* _type; - i32 _offset; - protected: - Location lazy_loc() override; - public: - LoadPtrInsn(Location src, const Type* t, i32 offset); - - void emit() override; - void format(stream& io) const override; - void liveout() override; - }; - - class StorePtrInsn : public Insn { - Location _dest, _src; - i32 _offset; - protected: - Location lazy_loc() override; - public: - StorePtrInsn(Location dest, Location src, i32 offset); - - void emit() override; - void format(stream& io) const override; - void liveout() override; - }; - - class AddressInsn : public Insn { - Location _src; - const Type* _type; - protected: - Location lazy_loc() override; - public: - AddressInsn(Location src, const Type* t); - - void emit() override; - void format(stream& io) const override; - void liveout() override; - }; - - class BinaryInsn : public Insn { - const char* _name; - protected: - Location _left, _right; - public: - BinaryInsn(InsnType kind, const char* name, Location left, Location right); - - void format(stream& io) const override; - void liveout() override; - Location& left(); - const Location& left() const; - Location& right(); - const Location& right() const; - }; - - class BinaryMathInsn : public BinaryInsn { - protected: - Location lazy_loc() override; - public: - BinaryMathInsn(InsnType kind, const char* name, Location left, - Location right); - }; - - class AddInsn : public BinaryMathInsn { - public: - AddInsn(Location left, Location right); - void emit() override; - }; - - class SubInsn : public BinaryMathInsn { - public: - SubInsn(Location left, Location right); - void emit() override; - }; - - class MulInsn : public BinaryMathInsn { - public: - MulInsn(Location left, Location right); - void emit() override; - }; - - class DivInsn : public BinaryMathInsn { - public: - DivInsn(Location left, Location right); - void emit() override; - }; - - class RemInsn : public BinaryMathInsn { - public: - RemInsn(Location left, Location right); - void emit() override; - }; - - class BinaryLogicInsn : public BinaryInsn { - protected: - Location lazy_loc() override; - public: - BinaryLogicInsn(InsnType kind, const char* name, Location left, - Location right); - }; - - class AndInsn : public BinaryLogicInsn { - public: - AndInsn(Location left, Location right); - void emit() override; - }; - - class OrInsn : public BinaryLogicInsn { - public: - OrInsn(Location left, Location right); - void emit() override; - }; - - class XorInsn : public BinaryLogicInsn { - public: - XorInsn(Location left, Location right); - void emit() override; - }; - - class NotInsn : public Insn { - Location _src; - protected: - Location lazy_loc() override; - public: - NotInsn(Location src); - - void emit() override; - void format(stream& io) const override; - void liveout() override; - }; - - class BinaryEqualityInsn : public BinaryInsn { - protected: - Location lazy_loc() override; - public: - BinaryEqualityInsn(InsnType kind, const char* name, Location left, - Location right); - }; - - class EqualInsn : public BinaryEqualityInsn { - public: - EqualInsn(Location left, Location right); - void emit() override; - }; - - class InequalInsn : public BinaryEqualityInsn { - public: - InequalInsn(Location left, Location right); - void emit() override; - }; - - class BinaryRelationInsn : public BinaryInsn { - protected: - Location lazy_loc() override; - public: - BinaryRelationInsn(InsnType kind, const char* name, Location left, - Location right); - }; - - class LessInsn : public BinaryRelationInsn { - public: - LessInsn(Location left, Location right); - void emit() override; - }; - - class LessEqualInsn : public BinaryRelationInsn { - public: - LessEqualInsn(Location left, Location right); - void emit() override; - }; - - class GreaterInsn : public BinaryRelationInsn { - public: - GreaterInsn(Location left, Location right); - void emit() override; - }; - - class GreaterEqualInsn : public BinaryRelationInsn { - public: - GreaterEqualInsn(Location left, Location right); - void emit() override; - }; - - class RetInsn : public Insn { - Location _src; - protected: - Location lazy_loc() override; - public: - RetInsn(Location src); - - void emit() override; - void format(stream& io) const override; - void liveout() override; - }; - - class LoadArgumentInsn : public Insn { - u32 _index; - const Type* _type; - protected: - Location lazy_loc() override; - public: - LoadArgumentInsn(u32 index, const Type* type); - - void emit() override; - void format(stream& io) const override; - void liveout() override; - }; - - class CallInsn : public Insn { - Location _fn; - vector _args; - const Type* _ret; - protected: - Location lazy_loc() override; - public: - CallInsn(Location fn, const vector& args, const Type* ret); - - void emit() override; - void format(stream& io) const override; - void liveout() override; - }; - - class Label : public Insn { - u32 _label; - protected: - Location lazy_loc() override; - public: - Label(u32 label); - - void emit() override; - void format(stream& io) const override; - void liveout() override; - }; - - class GotoInsn : public Insn { - u32 _label; - protected: - Location lazy_loc() override; - public: - GotoInsn(u32 label); - - void emit() override; - void format(stream& io) const override; - void liveout() override; - }; - - class IfZeroInsn : public Insn { - u32 _label; - Location _cond; - const Type* _result; - protected: - Location lazy_loc() override; - public: - IfZeroInsn(u32 label, Location cond); - - void emit() override; - void format(stream& io) const override; - void liveout() override; - }; -} + using namespace jasmine; + + enum LocationType { LOC_NONE, LOC_LOCAL, LOC_IMMEDIATE, LOC_CONSTANT, LOC_LABEL, LOC_REGISTER }; + + struct LocalInfo { + string name; + u32 index; + const Type* type; + i64 reg; + i64 offset; + }; + + struct ConstantInfo { + string name; + vector data; + const Type* type; + }; + + struct Location { + union { + u32 local_index; + i64 immediate; + u32 constant_index; + u32 label_index; + u32 reg; + }; + LocationType type; + + const Type* value_type(); + }; + + extern const u8 BINARY_INSN; + + enum InsnType { + LOAD_INSN = 0, + STORE_INSN = 1, + LOAD_ARG_INSN = 2, + GOTO_INSN = 3, + IFZERO_INSN = 4, + CALL_INSN = 5, + ADDRESS_INSN = 6, + NOT_INSN = 7, + LOAD_PTR_INSN = 8, + STORE_PTR_INSN = 9, + RET_INSN = 10, + LABEL = 11, + ADD_INSN = 128, + SUB_INSN = 129, + MUL_INSN = 130, + DIV_INSN = 131, + REM_INSN = 132, + AND_INSN = 133, + OR_INSN = 134, + XOR_INSN = 135, + EQ_INSN = 136, + NOT_EQ_INSN = 137, + LESS_INSN = 138, + LESS_EQ_INSN = 139, + GREATER_INSN = 140, + GREATER_EQ_INSN = 141, + }; + + Location loc_none(); + Location loc_immediate(i64 i); + Location loc_label(const string& label); + const Type* type_of(const Location& loc); + + i64 immediate_of(const Location& loc); + const string& label_of(const Location& loc); + LocalInfo& local_of(const Location& loc); + ConstantInfo& constant_of(const Location& loc); + + class Function; + + class Insn { + protected: + InsnType _kind; + Location _loc; + Function* _func; + vector _succ; + set _in, _out; + virtual Location lazy_loc() = 0; + + friend class Function; + void setfunc(Function* func); + + public: + Insn(InsnType kind); + virtual ~Insn(); + + InsnType kind() const; + const Location& loc() const; + Location& loc(); + virtual void emit() = 0; + virtual void format(stream& io) const = 0; + vector& succ(); + const vector& succ() const; + set& in(); + set& out(); + const set& in() const; + const set& out() const; + virtual void liveout() = 0; + }; + + u32 find_label(const string& label); + u32 add_label(const string& label); + u32 next_label(); + Location next_local(const Type* t); + Location const_loc(u32 label, const string& constant); + void emit_constants(Object& object); + + class Function { + vector _fns; + vector _insns; + i64 _stack; + vector _locals; + map _labels; + u32 _label; + u32 _end; + Location _ret; + void liveness(); + void to_registers(); + Function(u32 label); + + public: + Function(const string& label); + ~Function(); + + void place_label(u32 label); + Function& create_function(); + Function& create_function(const string& name); + Location create_local(const Type* t); + Location create_local(const string& name, const Type* t); + Location add(Insn* insn); + u32 label() const; + void allocate(); + void emit(Object& obj, bool exit = false); + void format(stream& io) const; + u32 end_label() const; + const Location& ret_loc() const; + Insn* last() const; + }; + + class LoadInsn : public Insn { + Location _src; + + protected: + Location lazy_loc() override; + + public: + LoadInsn(Location src); + + void emit() override; + void format(stream& io) const override; + void liveout() override; + }; + + class StoreInsn : public Insn { + Location _dest, _src; + + protected: + Location lazy_loc() override; + + public: + StoreInsn(Location dest, Location src); + + void emit() override; + void format(stream& io) const override; + void liveout() override; + }; + + class LoadPtrInsn : public Insn { + Location _src; + const Type* _type; + i32 _offset; + + protected: + Location lazy_loc() override; + + public: + LoadPtrInsn(Location src, const Type* t, i32 offset); + + void emit() override; + void format(stream& io) const override; + void liveout() override; + }; + + class StorePtrInsn : public Insn { + Location _dest, _src; + i32 _offset; + + protected: + Location lazy_loc() override; + + public: + StorePtrInsn(Location dest, Location src, i32 offset); + + void emit() override; + void format(stream& io) const override; + void liveout() override; + }; + + class AddressInsn : public Insn { + Location _src; + const Type* _type; + + protected: + Location lazy_loc() override; + + public: + AddressInsn(Location src, const Type* t); + + void emit() override; + void format(stream& io) const override; + void liveout() override; + }; + + class BinaryInsn : public Insn { + const char* _name; + + protected: + Location _left, _right; + + public: + BinaryInsn(InsnType kind, const char* name, Location left, Location right); + + void format(stream& io) const override; + void liveout() override; + Location& left(); + const Location& left() const; + Location& right(); + const Location& right() const; + }; + + class BinaryMathInsn : public BinaryInsn { + protected: + Location lazy_loc() override; + + public: + BinaryMathInsn(InsnType kind, const char* name, Location left, Location right); + }; + + class AddInsn : public BinaryMathInsn { + public: + AddInsn(Location left, Location right); + void emit() override; + }; + + class SubInsn : public BinaryMathInsn { + public: + SubInsn(Location left, Location right); + void emit() override; + }; + + class MulInsn : public BinaryMathInsn { + public: + MulInsn(Location left, Location right); + void emit() override; + }; + + class DivInsn : public BinaryMathInsn { + public: + DivInsn(Location left, Location right); + void emit() override; + }; + + class RemInsn : public BinaryMathInsn { + public: + RemInsn(Location left, Location right); + void emit() override; + }; + + class BinaryLogicInsn : public BinaryInsn { + protected: + Location lazy_loc() override; + + public: + BinaryLogicInsn(InsnType kind, const char* name, Location left, Location right); + }; + + class AndInsn : public BinaryLogicInsn { + public: + AndInsn(Location left, Location right); + void emit() override; + }; + + class OrInsn : public BinaryLogicInsn { + public: + OrInsn(Location left, Location right); + void emit() override; + }; + + class XorInsn : public BinaryLogicInsn { + public: + XorInsn(Location left, Location right); + void emit() override; + }; + + class NotInsn : public Insn { + Location _src; + + protected: + Location lazy_loc() override; + + public: + NotInsn(Location src); + + void emit() override; + void format(stream& io) const override; + void liveout() override; + }; + + class BinaryEqualityInsn : public BinaryInsn { + protected: + Location lazy_loc() override; + + public: + BinaryEqualityInsn(InsnType kind, const char* name, Location left, Location right); + }; + + class EqualInsn : public BinaryEqualityInsn { + public: + EqualInsn(Location left, Location right); + void emit() override; + }; + + class InequalInsn : public BinaryEqualityInsn { + public: + InequalInsn(Location left, Location right); + void emit() override; + }; + + class BinaryRelationInsn : public BinaryInsn { + protected: + Location lazy_loc() override; + + public: + BinaryRelationInsn(InsnType kind, const char* name, Location left, Location right); + }; + + class LessInsn : public BinaryRelationInsn { + public: + LessInsn(Location left, Location right); + void emit() override; + }; + + class LessEqualInsn : public BinaryRelationInsn { + public: + LessEqualInsn(Location left, Location right); + void emit() override; + }; + + class GreaterInsn : public BinaryRelationInsn { + public: + GreaterInsn(Location left, Location right); + void emit() override; + }; + + class GreaterEqualInsn : public BinaryRelationInsn { + public: + GreaterEqualInsn(Location left, Location right); + void emit() override; + }; + + class RetInsn : public Insn { + Location _src; + + protected: + Location lazy_loc() override; + + public: + RetInsn(Location src); + + void emit() override; + void format(stream& io) const override; + void liveout() override; + }; + + class LoadArgumentInsn : public Insn { + u32 _index; + const Type* _type; + + protected: + Location lazy_loc() override; + + public: + LoadArgumentInsn(u32 index, const Type* type); + + void emit() override; + void format(stream& io) const override; + void liveout() override; + }; + + class CallInsn : public Insn { + Location _fn; + vector _args; + const Type* _ret; + + protected: + Location lazy_loc() override; + + public: + CallInsn(Location fn, const vector& args, const Type* ret); + + void emit() override; + void format(stream& io) const override; + void liveout() override; + }; + + class Label : public Insn { + u32 _label; + + protected: + Location lazy_loc() override; + + public: + Label(u32 label); + + void emit() override; + void format(stream& io) const override; + void liveout() override; + }; + + class GotoInsn : public Insn { + u32 _label; + + protected: + Location lazy_loc() override; + + public: + GotoInsn(u32 label); + + void emit() override; + void format(stream& io) const override; + void liveout() override; + }; + + class IfZeroInsn : public Insn { + u32 _label; + Location _cond; + const Type* _result; + + protected: + Location lazy_loc() override; + + public: + IfZeroInsn(u32 label, Location cond); + + void emit() override; + void format(stream& io) const override; + void liveout() override; + }; +} // namespace basil void write(stream& io, const basil::Location& loc); void write(stream& io, basil::Insn* insn); diff --git a/compiler/lex.cpp b/compiler/lex.cpp index 4e20f1d..46aa695 100644 --- a/compiler/lex.cpp +++ b/compiler/lex.cpp @@ -1,175 +1,160 @@ #include "lex.h" -#include "util/io.h" -#include "errors.h" #include "ctype.h" -#include "stdlib.h" #include "driver.h" +#include "errors.h" +#include "stdlib.h" +#include "util/io.h" namespace basil { - Token::Token(TokenType type_in, const const_slice& value_in, u32 line_in, u32 column_in): - value(value_in), type(type_in), line(line_in), column(column_in) {} - - Token::operator bool() const { - return type != T_NONE; - } - - static const char* TOKEN_NAMES[NUM_TOKEN_TYPES] = { - "none", "int", "symbol", "string", "coeff", "float", "left paren", "right paren", - "access bracket", "left bracket", "right bracket", "left brace", "right brace", - "semicolon", "dot", "comma", "colon", "pipe", "plus", "minus", "quote", - "newline" - }; - - static Token NONE = Token(T_NONE, const_slice{0, nullptr}, 0, 0); - - static TokenType DELIMITERS[128] = { - T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 00 - T_NONE, T_NONE, T_NEWLINE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 08 - T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 10 - T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 18 - T_NONE, T_NONE, T_STRING, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 20 - T_LPAREN, T_RPAREN, T_NONE, T_NONE, T_COMMA, T_NONE, T_DOT, T_NONE, // 28 - T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 30 - T_NONE, T_NONE, T_COLON, T_SEMI, T_NONE, T_NONE, T_NONE, T_NONE, // 38 - T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 40 - T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 48 - T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 50 - T_NONE, T_NONE, T_NONE, T_LBRACK, T_NONE, T_RBRACK, T_NONE, T_NONE, // 58 - T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 60 - T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 68 - T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 70 - T_NONE, T_NONE, T_NONE, T_LBRACE, T_PIPE, T_RBRACE, T_NONE, T_NONE // 78 - }; - - bool isdelimiter(u8 ch) { - return DELIMITERS[ch]; - } - - // is valid char in symbol - bool issymbol(u8 ch) { - return isprint(ch) && !isdelimiter(ch) && !isspace(ch); - } - - // is valid first char in symbol - bool issymbolstart(u8 ch) { - return issymbol(ch) && !isdigit(ch) && ch != '_'; - } - - // is symbolic char (e.g. $, +, @), not a letter or digit - bool issymbolic(u8 ch) { - return issymbolstart(ch) && !isalpha(ch); - } - - Token scan(Source::View& view, bool follows_space) { - const u8* start = view.pos(); - u32 start_col = view.col(), line = view.line(); - u8 ch = view.peek(); - - if (!ch) { - return NONE; + Token::Token(TokenType type_in, const const_slice& value_in, u32 line_in, u32 column_in) + : value(value_in), type(type_in), line(line_in), column(column_in) {} + + Token::operator bool() const { + return type != T_NONE; } - else if (ch == '#') { - while (view.peek() && view.peek() != '\n') view.read(); - return scan(view); + + static const char* TOKEN_NAMES[NUM_TOKEN_TYPES] = {"none", "int", "symbol", "string", + "coeff", "float", "left paren", "right paren", + "access bracket", "left bracket", "right bracket", "left brace", + "right brace", "semicolon", "dot", "comma", + "colon", "pipe", "plus", "minus", + "quote", "newline"}; + + static Token NONE = Token(T_NONE, const_slice{0, nullptr}, 0, 0); + + static TokenType DELIMITERS[128] = { + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 00 + T_NONE, T_NONE, T_NEWLINE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 08 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 10 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 18 + T_NONE, T_NONE, T_STRING, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 20 + T_LPAREN, T_RPAREN, T_NONE, T_NONE, T_COMMA, T_NONE, T_DOT, T_NONE, // 28 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 30 + T_NONE, T_NONE, T_COLON, T_SEMI, T_NONE, T_NONE, T_NONE, T_NONE, // 38 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 40 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 48 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 50 + T_NONE, T_NONE, T_NONE, T_LBRACK, T_NONE, T_RBRACK, T_NONE, T_NONE, // 58 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 60 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 68 + T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, T_NONE, // 70 + T_NONE, T_NONE, T_NONE, T_LBRACE, T_PIPE, T_RBRACE, T_NONE, T_NONE // 78 + }; + + bool isdelimiter(u8 ch) { + return DELIMITERS[ch]; } - else if (ch == '"') { - view.read(); - while (view.peek() && view.peek() != '\n' && view.peek() != '"') { - if (view.read() == '\\' && view.peek() == '"') view.read(); - } - if (view.peek() != '"') - err({ line, u16(view.col()) }, - "Expected closing quote in string literal."); - else { - view.read(); - return Token(T_STRING, { u32(view.pos() - start), start }, - line, start_col); - } - } - else if (ch == '.') { - while (view.peek() == '.') view.read(); - u32 len = view.pos() - start; - return Token(len > 1 ? T_SYMBOL : T_DOT, { len, start }, line, start_col); + + // is valid char in symbol + bool issymbol(u8 ch) { + return isprint(ch) && !isdelimiter(ch) && !isspace(ch); } - else if (ch == ':') { - while (view.peek() == ':') view.read(); - u32 len = view.pos() - start; - if (len > 1) return Token(T_SYMBOL, { len, start }, line, start_col); - if (isspace(view.peek())) return Token(T_COLON, { 1, start }, line, start_col); - else return Token(T_QUOTE, { 1, start }, line, start_col); + + // is valid first char in symbol + bool issymbolstart(u8 ch) { + return issymbol(ch) && !isdigit(ch) && ch != '_'; } - else if (isdelimiter(ch)) { - view.read(); - TokenType type = DELIMITERS[ch]; - if (DELIMITERS[ch] == T_LBRACK && !follows_space && start_col > 0) type = T_ACCESS; - return Token(type, { 1, start }, line, start_col); + + // is symbolic char (e.g. $, +, @), not a letter or digit + bool issymbolic(u8 ch) { + return issymbolstart(ch) && !isalpha(ch); } - else if (issymbolstart(ch)) { - view.read(); - if (ch == '+' && !isspace(view.peek()) - && !issymbolic(view.peek())) - return Token(T_PLUS, { 1, start }, line, start_col); - else if (ch == '-' && !isspace(view.peek()) - && !issymbolic(view.peek())) - return Token(T_MINUS, { 1, start }, line, start_col); - - while (issymbol(view.peek())) view.read(); - return Token(T_SYMBOL, { u32(view.pos() - start), start }, line, start_col); + + Token scan(Source::View& view, bool follows_space) { + const u8* start = view.pos(); + u32 start_col = view.col(), line = view.line(); + u8 ch = view.peek(); + + if (!ch) { + return NONE; + } else if (ch == '#') { + while (view.peek() && view.peek() != '\n') view.read(); + return scan(view); + } else if (ch == '"') { + view.read(); + while (view.peek() && view.peek() != '\n' && view.peek() != '"') { + if (view.read() == '\\' && view.peek() == '"') view.read(); + } + if (view.peek() != '"') err({line, u16(view.col())}, "Expected closing quote in string literal."); + else { + view.read(); + return Token(T_STRING, {u32(view.pos() - start), start}, line, start_col); + } + } else if (ch == '.') { + while (view.peek() == '.') view.read(); + u32 len = view.pos() - start; + return Token(len > 1 ? T_SYMBOL : T_DOT, {len, start}, line, start_col); + } else if (ch == ':') { + while (view.peek() == ':') view.read(); + u32 len = view.pos() - start; + if (len > 1) return Token(T_SYMBOL, {len, start}, line, start_col); + if (isspace(view.peek())) return Token(T_COLON, {1, start}, line, start_col); + else + return Token(T_QUOTE, {1, start}, line, start_col); + } else if (isdelimiter(ch)) { + view.read(); + TokenType type = DELIMITERS[ch]; + if (DELIMITERS[ch] == T_LBRACK && !follows_space && start_col > 0) type = T_ACCESS; + return Token(type, {1, start}, line, start_col); + } else if (issymbolstart(ch)) { + view.read(); + if (ch == '+' && !isspace(view.peek()) && !issymbolic(view.peek())) + return Token(T_PLUS, {1, start}, line, start_col); + else if (ch == '-' && !isspace(view.peek()) && !issymbolic(view.peek())) + return Token(T_MINUS, {1, start}, line, start_col); + + while (issymbol(view.peek())) view.read(); + return Token(T_SYMBOL, {u32(view.pos() - start), start}, line, start_col); + } else if (isdigit(ch)) { + while (isdigit(view.peek())) view.read(); + if (issymbolstart(view.peek()) || view.peek() == '(' || view.peek() == '[' || view.peek() == '{') + return Token(T_COEFF, {u32(view.pos() - start), start}, line, start_col); + else if (isdelimiter(view.peek()) || isspace(view.peek())) + return Token(T_INT, {u32(view.pos() - start), start}, line, start_col); + else + err({line, u16(view.col())}, "Unexpected character in integer '", view.peek(), "'."); + } else if (isspace(ch)) { + view.read(); + return scan(view, true); + } else + err({line, u16(view.col())}, "Unexpected character in input '", view.read(), "'."); + return NONE; } - else if (isdigit(ch)) { - while (isdigit(view.peek())) view.read(); - if (issymbolstart(view.peek()) || view.peek() == '(' || view.peek() == '[' - || view.peek() == '{') - return Token(T_COEFF, { u32(view.pos() - start), start }, line, start_col); - else if (isdelimiter(view.peek()) || isspace(view.peek())) - return Token(T_INT, { u32(view.pos() - start), start }, line, start_col); - else - err({ line, u16(view.col()) }, "Unexpected character in integer '", view.peek(), "'."); + + TokenView::TokenView(vector& tokens, Source& source, bool repl) + : _tokens(&tokens), i(0), _source(&source), _repl(repl) {} + + const Token& TokenView::peek() { + return i < _tokens->size() ? (*_tokens)[i] : NONE; } - else if (isspace(ch)) { - view.read(); - return scan(view, true); + + const Token& TokenView::read() { + const Token& t = peek(); + i++; + return t; } - else err({ line, u16(view.col()) }, "Unexpected character in input '", view.read(), "'."); - return NONE; - } - - TokenView::TokenView(vector& tokens, - Source& source, bool repl): - _tokens(&tokens), i(0), _source(&source), _repl(repl) {} - - const Token& TokenView::peek() { - return i < _tokens->size() ? (*_tokens)[i] : NONE; - } - - const Token& TokenView::read() { - const Token& t = peek(); - i ++; - return t; - } - - void TokenView::rewind() { - if (i) i --; - } - - TokenView::operator bool() const { - return i < _tokens->size(); - } - - bool TokenView::repl() const { - return _repl; - } - - void TokenView::expand() { - print(". "); - Source::View view = _source->expand(_stdin); - - while (view.peek()) - _tokens->push(scan(view)); - } -} + + void TokenView::rewind() { + if (i) i--; + } + + TokenView::operator bool() const { + return i < _tokens->size(); + } + + bool TokenView::repl() const { + return _repl; + } + + void TokenView::expand() { + print(". "); + Source::View view = _source->expand(_stdin); + + while (view.peek()) _tokens->push(scan(view)); + } +} // namespace basil void write(stream& io, const basil::Token& t) { - string s = escape(t.value); - write(io, "[", basil::TOKEN_NAMES[t.type], ": '", s, "' : ", t.line, " ", t.column, "]"); + string s = escape(t.value); + write(io, "[", basil::TOKEN_NAMES[t.type], ": '", s, "' : ", t.line, " ", t.column, "]"); } diff --git a/compiler/lex.h b/compiler/lex.h index 8e7896a..6bf6a2e 100644 --- a/compiler/lex.h +++ b/compiler/lex.h @@ -1,49 +1,65 @@ #ifndef BASIL_LEX_H #define BASIL_LEX_H -#include "util/defs.h" #include "source.h" +#include "util/defs.h" namespace basil { - enum TokenType : u8 { - T_NONE, - T_INT, T_SYMBOL, T_STRING, T_COEFF, T_FLOAT, - T_LPAREN, T_RPAREN, T_ACCESS, T_LBRACK, T_RBRACK, T_LBRACE, T_RBRACE, - T_SEMI, T_DOT, T_COMMA, T_COLON, T_PIPE, - T_PLUS, T_MINUS, T_QUOTE, - T_NEWLINE, - NUM_TOKEN_TYPES - }; - - struct Token { - const_slice value; - TokenType type; - u32 line, column; - - Token(TokenType type_in, const const_slice& value_in, u32 line_in, u32 column_in); - operator bool() const; - }; - - class TokenView { - vector* _tokens; - u32 i; - Source* _source; - bool _repl; - public: - TokenView(vector& tokens, - Source& source, bool repl = false); - - const Token& peek(); - const Token& read(); - void rewind(); - operator bool() const; - bool repl() const; - void expand(); - }; - - Token scan(Source::View& view, bool follows_space = false); -} - -void write(stream& io, const basil::Token& t); + enum TokenType : u8 { + T_NONE, + T_INT, + T_SYMBOL, + T_STRING, + T_COEFF, + T_FLOAT, + T_LPAREN, + T_RPAREN, + T_ACCESS, + T_LBRACK, + T_RBRACK, + T_LBRACE, + T_RBRACE, + T_SEMI, + T_DOT, + T_COMMA, + T_COLON, + T_PIPE, + T_PLUS, + T_MINUS, + T_QUOTE, + T_NEWLINE, + NUM_TOKEN_TYPES + }; + + struct Token { + const_slice value; + TokenType type; + u32 line, column; + + Token(TokenType type_in, const const_slice& value_in, u32 line_in, u32 column_in); + operator bool() const; + }; + + class TokenView { + vector* _tokens; + u32 i; + Source* _source; + bool _repl; + + public: + TokenView(vector& tokens, Source& source, bool repl = false); + + const Token& peek(); + const Token& read(); + void rewind(); + operator bool() const; + bool repl() const; + void expand(); + }; + + Token scan(Source::View& view, bool follows_space = false); +} // namespace basil + +void write(stream& io, const basil::Token& t); #endif diff --git a/compiler/main.cpp b/compiler/main.cpp index 1a210a5..f1e2465 100644 --- a/compiler/main.cpp +++ b/compiler/main.cpp @@ -1,140 +1,134 @@ -#include "source.h" -#include "eval.h" -#include "values.h" -#include "driver.h" #include "ast.h" #include "builtin.h" +#include "driver.h" +#include "eval.h" +#include "source.h" #include "unistd.h" +#include "values.h" using namespace basil; void print_banner() { - println(""); - println("┌────────────────────────────────────────┐"); - println("│ │"); - println("│ ", BOLDGREEN, R"(𝐵𝑎𝑠𝑖𝑙)", - RESET, " — version 0.1 │"); - println("│ │"); - println("└────────────────────────────────────────┘"); - println(RESET); + println(""); + println("┌────────────────────────────────────────┐"); + println("│ │"); + println("│ ", BOLDGREEN, R"(𝐵𝑎𝑠𝑖𝑙)", RESET, " — version 0.1 │"); + println("│ │"); + println("└────────────────────────────────────────┘"); + println(RESET); } static bool repl_done = false; Value repl_quit(ref env, const Value& args) { - repl_done = true; - return Value(VOID); + repl_done = true; + return Value(VOID); } Value print_tokens(ref env, const Value& args) { - basil::print_tokens(args.get_product()[0].get_bool()); - return Value(VOID); + basil::print_tokens(args.get_product()[0].get_bool()); + return Value(VOID); } Value print_parse(ref env, const Value& args) { - basil::print_parsed(args.get_product()[0].get_bool()); - return Value(VOID); + basil::print_parsed(args.get_product()[0].get_bool()); + return Value(VOID); } Value print_ast(ref env, const Value& args) { - basil::print_ast(args.get_product()[0].get_bool()); - return Value(VOID); + basil::print_ast(args.get_product()[0].get_bool()); + return Value(VOID); } Value print_ir(ref env, const Value& args) { - basil::print_ir(args.get_product()[0].get_bool()); - return Value(VOID); + basil::print_ir(args.get_product()[0].get_bool()); + return Value(VOID); } Value print_asm(ref env, const Value& args) { - basil::print_asm(args.get_product()[0].get_bool()); - return Value(VOID); + basil::print_asm(args.get_product()[0].get_bool()); + return Value(VOID); } Value repl_help(ref env, const Value& args) { - println(""); - println("Commands: "); - println(" - $help => prints this command list."); - println(" - $quit => exits REPL."); - println(" - $print-tokens => toggles token printing."); - println(" - $print-parse => toggles parse tree printing."); - println(" - $print-ast => toggles AST printing."); - println(" - $print-ir => toggles IR printing."); - println(" - $print-asm => toggles assembly printing."); - println(""); - return Value(VOID); + println(""); + println("Commands: "); + println(" - $help => prints this command list."); + println(" - $quit => exits REPL."); + println(" - $print-tokens => toggles token printing."); + println(" - $print-parse => toggles parse tree printing."); + println(" - $print-ast => toggles AST printing."); + println(" - $print-ir => toggles IR printing."); + println(" - $print-asm => toggles assembly printing."); + println(""); + return Value(VOID); } int intro(); int main(int argc, char** argv) { - Builtin REPL_HELP(find(find(), VOID), repl_help, nullptr), - REPL_QUIT(find(find(), VOID), repl_quit, nullptr), - PRINT_TOKENS(find(find(BOOL), VOID), print_tokens, nullptr), - PRINT_PARSE(find(find(BOOL), VOID), print_parse, nullptr), - PRINT_AST(find(find(BOOL), VOID), print_ast, nullptr), - PRINT_IR(find(find(BOOL), VOID), print_ir, nullptr), - PRINT_ASM(find(find(BOOL), VOID), print_asm, nullptr); - - if (argc == 1) { // repl mode - print_banner(); - println(BOLDGREEN, "Enter any Basil expression at the prompt, or '", - RESET, "$help", BOLDGREEN, "' for a list of commands!", RESET); - println(""); - Source src; - - ref root = create_root_env(); - root->def("$help", Value(root, REPL_HELP), 0); - root->def("$quit", Value(root, REPL_QUIT), 0); - root->def("$print-tokens", Value(root, PRINT_TOKENS), 1); - root->def("$print-parse", Value(root, PRINT_PARSE), 1); - root->def("$print-ast", Value(root, PRINT_AST), 1); - root->def("$print-ir", Value(root, PRINT_IR), 1); - root->def("$print-asm", Value(root, PRINT_ASM), 1); - ref global = newref(root); - Function main_fn("main"); - - while (!repl_done) repl(global, src, main_fn), clear_errors(); - return 0; - } - else if (string(argv[1]) == "intro") { - return intro(); - } - else if (argc == 3 && string(argv[1]) == "run") { - Source src(argv[2]); - return run(src); - } - else if (argc == 3 && string(argv[1]) == "build") { - Source src(argv[2]); - return build(src, argv[2]); - } - else if (argc > 2 && string(argv[1]) == "exec") { - Source src; - string code; - for (int i = 2; i < argc; i ++) { - code += argv[i]; - code += ' '; - } - src.add_line(code); - return run(src); - } - else if (string(argv[1]) != "help") { - Source src(argv[1]); - return run(src); - } - - print_banner(); - - println(BOLDGREEN, "Welcome to the Basil programming language!", RESET); - println(""); - println("Commands: "); - println(" - basil => starts REPL."); - println(" - basil => executes ."); - println(" - basil help => prints usage information."); - println(" - basil intro => runs interactive introduction."); - println(" - basil exec => executes ."); - println(" - basil build => compiles to object."); - println(""); + Builtin REPL_HELP(find(find(), VOID), repl_help, nullptr), + REPL_QUIT(find(find(), VOID), repl_quit, nullptr), + PRINT_TOKENS(find(find(BOOL), VOID), print_tokens, nullptr), + PRINT_PARSE(find(find(BOOL), VOID), print_parse, nullptr), + PRINT_AST(find(find(BOOL), VOID), print_ast, nullptr), + PRINT_IR(find(find(BOOL), VOID), print_ir, nullptr), + PRINT_ASM(find(find(BOOL), VOID), print_asm, nullptr); + + if (argc == 1) { // repl mode + print_banner(); + println(BOLDGREEN, "Enter any Basil expression at the prompt, or '", RESET, "$help", BOLDGREEN, + "' for a list of commands!", RESET); + println(""); + Source src; + + ref root = create_root_env(); + root->def("$help", Value(root, REPL_HELP), 0); + root->def("$quit", Value(root, REPL_QUIT), 0); + root->def("$print-tokens", Value(root, PRINT_TOKENS), 1); + root->def("$print-parse", Value(root, PRINT_PARSE), 1); + root->def("$print-ast", Value(root, PRINT_AST), 1); + root->def("$print-ir", Value(root, PRINT_IR), 1); + root->def("$print-asm", Value(root, PRINT_ASM), 1); + ref global = newref(root); + Function main_fn("main"); + + while (!repl_done) repl(global, src, main_fn), clear_errors(); + return 0; + } else if (string(argv[1]) == "intro") { + return intro(); + } else if (argc == 3 && string(argv[1]) == "run") { + Source src(argv[2]); + return run(src); + } else if (argc == 3 && string(argv[1]) == "build") { + Source src(argv[2]); + return build(src, argv[2]); + } else if (argc > 2 && string(argv[1]) == "exec") { + Source src; + string code; + for (int i = 2; i < argc; i++) { + code += argv[i]; + code += ' '; + } + src.add_line(code); + return run(src); + } else if (string(argv[1]) != "help") { + Source src(argv[1]); + return run(src); + } + + print_banner(); + + println(BOLDGREEN, "Welcome to the Basil programming language!", RESET); + println(""); + println("Commands: "); + println(" - basil => starts REPL."); + println(" - basil => executes ."); + println(" - basil help => prints usage information."); + println(" - basil intro => runs interactive introduction."); + println(" - basil exec => executes ."); + println(" - basil build => compiles to object."); + println(""); } static bool running_intro = false; @@ -142,151 +136,154 @@ static i64 intro_section = 0; static i64 intro_section_index = 0; Value intro_start(ref env, const Value& args) { - running_intro = true; - return Value(VOID); + running_intro = true; + return Value(VOID); } Value intro_help(ref env, const Value& args) { - println(""); - println("Commands: "); - println(" - $help => prints this command list."); - println(" - $quit => exits tour."); - println(" - $start => starts tour."); - println(" - $contents => prints table of contents."); - println(" - $section => skips to section with number ."); - println(""); - return Value(VOID); + println(""); + println("Commands: "); + println(" - $help => prints this command list."); + println(" - $quit => exits tour."); + println(" - $start => starts tour."); + println(" - $contents => prints table of contents."); + println(" - $section => skips to section with number ."); + println(""); + return Value(VOID); } Value intro_contents(ref env, const Value& args) { - println(""); - println("Sections: "); - println(BOLDWHITE, "1. Basics", RESET); - println(" - Hello, World!"); - println(" - Constants"); - println(" - Lists"); - println(" - Expressions"); - println(" - Indentation"); - println(" - Quotation"); - println(BOLDWHITE, "2. Definitions", RESET); - println(" - Variables"); - println(" - Procedures"); - println(" - Lambdas"); - println(BOLDWHITE, "3. Arithmetic and Logic", RESET); - println(" - Arithmetic Operators"); - println(" - Booleans"); - println(" - Logical Operators"); - println(" - Comparisons"); - println(BOLDWHITE, "4. Infix Operators", RESET); - println(" - Binary Operators"); - println(" - Unary Operators"); - println(" - Higher-Arity Operators"); - println(" - Mixfix Procedures"); - println(BOLDWHITE, "5. Control Flow", RESET); - println(" - If Expressions"); - println(" - While Loops"); - println(BOLDWHITE, "6. Metaprogramming", RESET); - println(" - Splices"); - println(" - Aliases"); - println(" - Macro Procedures"); - println(" - Macro Operators"); - println(BOLDWHITE, "7. Imports", RESET); - println(" - Use Statement"); - println(""); - return Value(VOID); + println(""); + println("Sections: "); + println(BOLDWHITE, "1. Basics", RESET); + println(" - Hello, World!"); + println(" - Constants"); + println(" - Lists"); + println(" - Expressions"); + println(" - Indentation"); + println(" - Quotation"); + println(BOLDWHITE, "2. Definitions", RESET); + println(" - Variables"); + println(" - Procedures"); + println(" - Lambdas"); + println(BOLDWHITE, "3. Arithmetic and Logic", RESET); + println(" - Arithmetic Operators"); + println(" - Booleans"); + println(" - Logical Operators"); + println(" - Comparisons"); + println(BOLDWHITE, "4. Infix Operators", RESET); + println(" - Binary Operators"); + println(" - Unary Operators"); + println(" - Higher-Arity Operators"); + println(" - Mixfix Procedures"); + println(BOLDWHITE, "5. Control Flow", RESET); + println(" - If Expressions"); + println(" - While Loops"); + println(BOLDWHITE, "6. Metaprogramming", RESET); + println(" - Splices"); + println(" - Aliases"); + println(" - Macro Procedures"); + println(" - Macro Operators"); + println(BOLDWHITE, "7. Imports", RESET); + println(" - Use Statement"); + println(""); + return Value(VOID); } Value intro_set_section(ref env, const Value& args) { - if (!args.get_product()[0].is_int()) { - err(args.get_product()[0].loc(), "Expected integer for section number, given ", - args.get_product()[0]); - return Value(ERROR); - } - if (args.get_product()[0].get_int() < 1 - || args.get_product()[0].get_int() > 7) { - err(args.get_product()[0].loc(), "Invalid section number. Must be between 1 ", - "and 7."); - return Value(ERROR); - } - intro_section = args.get_product()[0].get_int() - 1; - intro_section_index = 0; - return intro_start(env, args); + if (!args.get_product()[0].is_int()) { + err(args.get_product()[0].loc(), "Expected integer for section number, given ", args.get_product()[0]); + return Value(ERROR); + } + if (args.get_product()[0].get_int() < 1 || args.get_product()[0].get_int() > 7) { + err(args.get_product()[0].loc(), "Invalid section number. Must be between 1 ", "and 7."); + return Value(ERROR); + } + intro_section = args.get_product()[0].get_int() - 1; + intro_section_index = 0; + return intro_start(env, args); } struct IntroStep { - const char* text; - const char* instruction; - Value expected; - const char* title = nullptr; + const char* text; + const char* instruction; + Value expected; + const char* title = nullptr; }; struct IntroSection { - vector steps; + vector steps; }; static vector intro_sections; void add_step(IntroStep step) { - intro_sections.back().steps.push(step); + intro_sections.back().steps.push(step); } Value runtime(const Type* t) { - return new ASTSingleton(t); + return new ASTSingleton(t); } void init_intro_sections() { - intro_sections.push({}); // Basics - add_step({ -R"(Welcome to Basil! + intro_sections.push({}); // Basics + add_step({ + R"(Welcome to Basil! Basil is a lightweight programming language, which aims to make it as easy as possible to write expressive and performant code. We'll talk about some of the details later, but for now, let's write a hello-world program! -)", -R"(Type 'display "hello world"' into the prompt, then +)", + R"(Type 'display "hello world"' into the prompt, then press Enter twice. -)", runtime(VOID), "Basics"}); - add_step({ -R"(All Basil programs are made up of terms. The simplest +)", + runtime(VOID), "Basics"}); + add_step({ + R"(All Basil programs are made up of terms. The simplest terms are constants, which evaluate to themselves. For example, an integer constant. -)", -R"(Type '12' into the prompt, then press Enter twice. -)", Value(12)}); - add_step({ -R"(In addition to integer constants, which represent +)", + R"(Type '12' into the prompt, then press Enter twice. +)", + Value(12)}); + add_step({ + R"(In addition to integer constants, which represent numbers, Basil supports string constants, which represent pieces of text. -)", -R"(Type '"hello"' into the prompt, then press Enter twice. -)", Value("hello", STRING)}); - add_step({ -R"(Basil also supports values that contain multiple terms. +)", + R"(Type '"hello"' into the prompt, then press Enter twice. +)", + Value("hello", STRING)}); + add_step({ + R"(Basil also supports values that contain multiple terms. These values are called lists. If you're familiar with any Lisp dialects, these will probably be pretty familiar to you. To express a list value, we enclose other terms in square brackets. -)", -R"(Type '[1 2 3]' into the prompt, then press Enter twice. -)", list_of(1, 2, 3)}); - add_step({ -R"(The empty list is a little special in Basil, but it can +)", + R"(Type '[1 2 3]' into the prompt, then press Enter twice. +)", + list_of(1, 2, 3)}); + add_step({ + R"(The empty list is a little special in Basil, but it can be written about as you'd expect. It generally represents the absence of a value. -)", -R"(Type '[]' into the prompt, then press Enter twice. -)", empty()}); - add_step({ -R"(Lists can be useful for storing data, but they can also +)", + R"(Type '[]' into the prompt, then press Enter twice. +)", + empty()}); + add_step({ + R"(Lists can be useful for storing data, but they can also be used to represent code. Basil programs themselves are just lists of terms that the Basil compiler evaluates. To write a list we want to be evaluated, we enclose other terms in parentheses, and separate them with spaces. )", -R"(Type '(+ 1 2)' into the prompt, then press Enter twice. -)", Value(3)}); - add_step({ -R"(This is an example of evaluation. '+' is a symbol that + R"(Type '(+ 1 2)' into the prompt, then press Enter twice. +)", + Value(3)}); + add_step({ + R"(This is an example of evaluation. '+' is a symbol that represents a built-in function to add two numbers. When we evaluate a list that starts with a function, the result of evaluation is the result of that function. So, (+ 1 2) @@ -297,252 +294,279 @@ Basil treats lines on the top level of the program as lists, as if they were surrounded by parentheses. We still need to separate every term with spaces, though. )", -R"(Type '* 3 3' into the prompt, then press Enter twice. -)", Value(9)}); - add_step({ -R"(Basil programs can contain multiple terms. This is + R"(Type '* 3 3' into the prompt, then press Enter twice. +)", + Value(9)}); + add_step({ + R"(Basil programs can contain multiple terms. This is why you have to press Enter twice at the REPL - to be able to enter multiple terms at once. The program evaluates every term, then returns the last result. )", -R"(Type: + R"(Type: 1 2 3 into the prompt, pressing Enter after each one. Press Enter again after the last line. -)", Value(3)}); - add_step({ -R"(We can write expressions that evaluate others this way +)", + Value(3)}); + add_step({ + R"(We can write expressions that evaluate others this way using the 'do' special form. 'do' will evaluate each expression in its body, then return the last result - in a way that can be used in other expressions, like '*'. )", -R"(Type '* 2 (do 1 2 3)' into the prompt, then press Enter twice. -)", Value(6)}); - add_step({ -R"(Sometimes, we want to 'do' quite a lot of things. In + R"(Type '* 2 (do 1 2 3)' into the prompt, then press Enter twice. +)", + Value(6)}); + add_step({ + R"(Sometimes, we want to 'do' quite a lot of things. In these cases, it can be helpful to split a line into multiple using an indented block. The indentation level doesn't matter, as long as it's consistently more than the starting line. )", -R"(Type: + R"(Type: do 1 2 3 into the prompt, then press Enter twice. -)", Value(3)}); - add_step({ -R"(So, we evaluate terms to get values, and those values +)", + Value(3)}); + add_step({ + R"(So, we evaluate terms to get values, and those values are the result of our program. But what if we want to get the value of some bit of code before it's evaluated? For this, we use the 'quote' special form. )", -R"(Type 'quote x' into the prompt, then press Enter twice. -)", Value("x")}); - add_step({ -R"(We 'quote' x before it's evaluated, and get a symbol + R"(Type 'quote x' into the prompt, then press Enter twice. +)", + Value("x")}); + add_step({ + R"(We 'quote' x before it's evaluated, and get a symbol value. Symbols represent unique names, not bits of text like strings. The 'quote' special form can be abbreviated with a ':'. )", -R"(Type ':x' into the prompt, then press Enter twice. -)", Value("x")}); - add_step({ -R"(That's the basics! But there's a lot more to Basil that + R"(Type ':x' into the prompt, then press Enter twice. +)", + Value("x")}); + add_step({ + R"(That's the basics! But there's a lot more to Basil that we'll cover in the next sections. )", -R"(Enter ':okay!' into the prompt when you're ready to continue. -)", Value("okay!")}); - intro_sections.push({}); // Definitions - add_step({ -R"(We've discussed how to write simple expressions and + R"(Enter ':okay!' into the prompt when you're ready to continue. +)", + Value("okay!")}); + intro_sections.push({}); // Definitions + add_step({ + R"(We've discussed how to write simple expressions and constants in Basil up until this point. But for more complex programs, we might want to assign certain values names. We can do just that with the 'def' special form. -)", -R"(Enter: +)", + R"(Enter: def x 1 x into the prompt. -)", Value(1), "Definitions"}); - add_step({ -R"('def' defines a variable in an environment. When a +)", + Value(1), "Definitions"}); + add_step({ + R"('def' defines a variable in an environment. When a symbol is evaluated in an environment, it evaluates to the value of the associated variable, if it exists. This value can be used in arbitrary expressions. -)", -R"(Enter: +)", + R"(Enter: def x 1 def y 2 + x y into the prompt. -)", Value(3)}); - add_step({ -R"(We can also define procedures. Procedures take arguments, +)", + Value(3)}); + add_step({ + R"(We can also define procedures. Procedures take arguments, and produce a resulting value. We've seen a few built-in procedures so far, like '+' and '*'. Now, we can define our own procedures! -)", -R"(Enter: +)", + R"(Enter: def (square x) * x x square 12 into the prompt. -)", Value(144)}); - add_step({ -R"(We can add more arguments to a procedure by adding them +)", + Value(144)}); + add_step({ + R"(We can add more arguments to a procedure by adding them to the list after the 'def'. -)", -R"(Enter: +)", + R"(Enter: def (add-squares x y) + (* x x) (* y y) add-squares 3 4 into the prompt. -)", Value(25)}); - add_step({ -R"(If a procedure has multiple terms in its body, it will +)", + Value(25)}); + add_step({ + R"(If a procedure has multiple terms in its body, it will return the last result, as if they were wrapped in a 'do'. -)", -R"(Enter: +)", + R"(Enter: def (print x y) display x display y print 1 2 into the prompt. -)", runtime(VOID)}); - add_step({ -R"(If a procedure has no arguments, it will evaluate without +)", + runtime(VOID)}); + add_step({ + R"(If a procedure has no arguments, it will evaluate without arguments. -)", -R"(Enter: +)", + R"(Enter: def (greet) display "hello world" greet into the prompt. -)", runtime(VOID)}); - add_step({ -R"(Sometimes, we might want to produce a function value +)", + runtime(VOID)}); + add_step({ + R"(Sometimes, we might want to produce a function value without giving it a name. In these cases, we can use the 'lambda' special form to define an anonymous function. -)", -R"(Enter '(lambda (x y) (+ x y)) 1 2' into the prompt. -)", Value(3)}); - add_step({ -R"(That's how definitions work! Next, we'll go over the +)", + R"(Enter '(lambda (x y) (+ x y)) 1 2' into the prompt. +)", + Value(3)}); + add_step({ + R"(That's how definitions work! Next, we'll go over the built-in operations Basil supports, before getting into control flow in the section after. )", -R"(Enter ':okay!' into the prompt when you're ready to continue. -)", Value("okay!")}); - intro_sections.push({}); // Arithmetic and such - add_step({ -R"(So far we've introduced the '+' and '*' operators, + R"(Enter ':okay!' into the prompt when you're ready to continue. +)", + Value("okay!")}); + intro_sections.push({}); // Arithmetic and such + add_step({ + R"(So far we've introduced the '+' and '*' operators, which add and multiply numbers. Basil also supports '-', '/', and '%', each of which take two numbers and find the difference, quotient, or remainder respectively. -)", -R"(Enter any expression that evaluates to '81' into the prompt. -)", Value(81), "Arithmetic and Logic"}); - add_step({ -R"(Basil includes syntactic sugar for prefix '-' and +)", + R"(Enter any expression that evaluates to '81' into the prompt. +)", + Value(81), "Arithmetic and Logic"}); + add_step({ + R"(Basil includes syntactic sugar for prefix '-' and '+'. If either of these are written before a term (with no space), they act as unary minus and plus operators. -)", -R"(Enter any expression that evaluates to '-16' into the prompt. -)", Value(-16)}); - add_step({ -R"(Basil also includes syntactic sugar for coefficients. +)", + R"(Enter any expression that evaluates to '-16' into the prompt. +)", + Value(-16)}); + add_step({ + R"(Basil also includes syntactic sugar for coefficients. If a number is written immediately before another term (with no space), it will multiply the term by the number. -)", -R"(Enter: +)", + R"(Enter: def x 4 3x + 1 into the prompt. -)", Value(13)}); - add_step({ -R"(In addition to numbers, Basil also has support for a +)", + Value(13)}); + add_step({ + R"(In addition to numbers, Basil also has support for a boolean type, which can be either of the values 'true' or 'false'. -)", -R"(Enter 'true' into the prompt. -)", Value(true, BOOL)}); - add_step({ -R"(The 'and', 'or', and 'xor' procedures can be used to +)", + R"(Enter 'true' into the prompt. +)", + Value(true, BOOL)}); + add_step({ + R"(The 'and', 'or', and 'xor' procedures can be used to compute the logical and, inclusive-or, or exclusive-or of two booleans respectively.. -)", -R"(Enter 'or (and true false) (xor true true)' into the prompt. -)", Value(false, BOOL)}); - add_step({ -R"(The 'not' procedure can be used to compute the logical +)", + R"(Enter 'or (and true false) (xor true true)' into the prompt. +)", + Value(false, BOOL)}); + add_step({ + R"(The 'not' procedure can be used to compute the logical complement of a boolean value. -)", -R"(Enter 'not false' into the prompt. -)", Value(true, BOOL)}); - add_step({ -R"(We can get boolean values by comparing integers or +)", + R"(Enter 'not false' into the prompt. +)", + Value(true, BOOL)}); + add_step({ + R"(We can get boolean values by comparing integers or strings. The '==' and '!=' procedures return whether two inputs are equal or inequal, respectively. -)", -R"(Enter '== "cat" "cat"' into the prompt. -)", Value(true, BOOL)}); - add_step({ -R"(We can also compare values by some order. The '<' +)", + R"(Enter '== "cat" "cat"' into the prompt. +)", + Value(true, BOOL)}); + add_step({ + R"(We can also compare values by some order. The '<' and '>' procedures return whether the first argument is less than or greater than the second, respectively. '<=' and '>=' behave the same as '<' and '>', but also return true if the arguments are equal. -)", -R"(Enter '< 12 7' into the prompt. -)", Value(false, BOOL)}); - add_step({ -R"(That's all the boolean and arithmetic operators +)", + R"(Enter '< 12 7' into the prompt. +)", + Value(false, BOOL)}); + add_step({ + R"(That's all the boolean and arithmetic operators Basil supports! Next up, we'll get into infix operators, a more unique Basil feature to help write these expressions more naturally. )", -R"(Enter ':okay!' into the prompt when you're ready to continue. -)", Value("okay!")}); - intro_sections.push({}); // Infix operators - add_step({ -R"(Until this point, all expressions have been written + R"(Enter ':okay!' into the prompt when you're ready to continue. +)", + Value("okay!")}); + intro_sections.push({}); // Infix operators + add_step({ + R"(Until this point, all expressions have been written in 'prefix notation', with the procedure coming first, followed by some arguments. This is great for parsers, but it can be kind of unfamiliar, and inconvenient! Basil supports this notation, but also allows some procedures to be used 'infix', placed in-between their arguments. -)", -R"(Enter '1 + 2' into the prompt. -)", Value(3), "Infix Operators"}); - add_step({ -R"(All of the arithmetic and logical operators we've +)", + R"(Enter '1 + 2' into the prompt. +)", + Value(3), "Infix Operators"}); + add_step({ + R"(All of the arithmetic and logical operators we've seen so far can be used infix. To determine which operator applies to what, each operator has a certain precedence. Operators of higher precedence, like '*', bind more tightly than '+'. -)", -R"(Enter '1 + 2 * 3' into the prompt. -)", Value(7)}); - add_step({ -R"(Chains of infix operators are actually translated +)", + R"(Enter '1 + 2 * 3' into the prompt. +)", + Value(7)}); + add_step({ + R"(Chains of infix operators are actually translated by the compiler into equivalent prefix notation before they are evaluated. This happens in standalone lists, like in the previous two examples, and also in the argument lists of procedure calls or special forms. -)", -R"(Enter: +)", + R"(Enter: def (double x) x * 2 double 10 / 5 into the prompt. -)", Value(4)}); - add_step({ -R"(Infix operators can be defined by the user as well. +)", + Value(4)}); + add_step({ + R"(Infix operators can be defined by the user as well. This is done with the 'infix' special form. 'infix' operates very similarly to 'def', but with two key differences: @@ -550,56 +574,60 @@ operates very similarly to 'def', but with two key symbol, to be used as the operator's precedence. - The name of the procedure is the second element of the argument list, not the first. -)", -R"(Enter: +)", + R"(Enter: infix (x add y) x + y 1 add 2 add 3 into the prompt. -)", Value(6)}); - add_step({ -R"(Infix operators don't have to be binary operators. +)", + Value(6)}); + add_step({ + R"(Infix operators don't have to be binary operators. A unary infix operator behaves like a postfix function. -)", -R"(Enter: +)", + R"(Enter: infix (x squared) x * x 13 squared into the prompt. -)", Value(169)}); - add_step({ -R"(Infix operators can have more than two arguments too. +)", + Value(169)}); + add_step({ + R"(Infix operators can have more than two arguments too. The operator is still written after the first argument, but more than one argument may follow it. -)", -R"(Enter: +)", + R"(Enter: infix (s scale x y z) [x * s y * s z * s] 2 scale 4 5 6 into the prompt. -)", list_of(8, 10, 12)}); - add_step({ -R"(Basil also supports a lesser-known concept known as +)", + list_of(8, 10, 12)}); + add_step({ + R"(Basil also supports a lesser-known concept known as mixfix syntax. Every Basil procedure definition (so, both 'def' and 'infix') allows one or more keywords in the argument list. These keywords aren't arguments, but must be present in the function's invocation. This can help make functions with many arguments more readable. -)", -R"(Enter: +)", + R"(Enter: def (add x :to y) x + y add 1 to 2 into the prompt. -)", Value(3)}); - add_step({ -R"(Mixfix syntax can allow a single function invocation +)", + Value(3)}); + add_step({ + R"(Mixfix syntax can allow a single function invocation to span multiple lines. This only occurs when the following lines start with keywords of said function. In order for this to work, the keywords also need to be explicitly quoted. -)", -R"(Enter: +)", + R"(Enter: def (first x :then y :finally z) x y @@ -611,176 +639,192 @@ R"(Enter: :finally "goodbye" into the prompt. -)", Value("goodbye", STRING)}); - add_step({ -R"(That's the overview of infix and mixfix procedures! +)", + Value("goodbye", STRING)}); + add_step({ + R"(That's the overview of infix and mixfix procedures! We think these are some of the coolest features Basil has to offer. A lot of common programming patterns can be expressed in very natural terms, without sacrificing homoiconicity or performance. Next, we'll be going over Basil's control-flow primitives. )", -R"(Enter ':okay!' into the prompt when you're ready to continue. -)", Value("okay!")}); - intro_sections.push({}); // Control flow - add_step({ -R"(The simplest conditional expression Basil supports + R"(Enter ':okay!' into the prompt when you're ready to continue. +)", + Value("okay!")}); + intro_sections.push({}); // Control flow + add_step({ + R"(The simplest conditional expression Basil supports is the '?' operator. '?' is an infix, ternary operator, that takes a condition and two pieces of code. If the condition is true, it evaluates and returns the first; otherwise, it evaluates and returns the second. -)", -R"(Enter '4 < 5 ? "less" "greater"' into the prompt. -)", Value("less", STRING), "Control Flow"}); - add_step({ -R"(For more complex conditionals, we found that it was +)", + R"(Enter '4 < 5 ? "less" "greater"' into the prompt. +)", + Value("less", STRING), "Control Flow"}); + add_step({ + R"(For more complex conditionals, we found that it was nice to have a little more structure. So the 'if' special form exists as an alternative to '?'. 'if', like '?', picks between two pieces of code based on a condition. For 'if', though, the case if false needs to be preceded by the keyword 'else'. -)", -R"(Enter 'if true :yes else :no"' into the prompt. -)", Value("yes")}); - add_step({ -R"('if' can have multiple conditions in the same list, +)", + R"(Enter 'if true :yes else :no"' into the prompt. +)", + Value("yes")}); + add_step({ + R"('if' can have multiple conditions in the same list, using any number of 'elif' keywords. Each 'elif' behaves like its own 'if', within the larger expression. -)", -R"(Enter: +)", + R"(Enter: def x 2 if x == 1 "one" :elif x == 2 "two" :else "many" into the prompt. -)", Value("two", STRING)}); - add_step({ -R"(Unlike '?', 'if' can have multiple terms in the +)", + Value("two", STRING)}); + add_step({ + R"(Unlike '?', 'if' can have multiple terms in the body of each conditional. If multiple terms are provided, each is evaluated and the last result is returned when the corresponding condition is true. -)", -R"(Enter: +)", + R"(Enter: if true display "it's true!" display "hooray!" :else display "it's false..." into the prompt. -)", runtime(VOID)}); - add_step({ -R"(Basil also has a 'while' special form. This +)", + runtime(VOID)}); + add_step({ + R"(Basil also has a 'while' special form. This operates similarly to if, but instead of evaluating its body once, it will evaluate its body until its condition is false. -)", -R"(Enter: +)", + R"(Enter: def x 0 while x < 10 display x x = x + 1 into the prompt. -)", runtime(VOID)}); - add_step({ -R"(That's all the built-in control flow Basil has! +)", + runtime(VOID)}); + add_step({ + R"(That's all the built-in control flow Basil has! We intentionally kept it short, since other Basil features allow more specific control flow to be user-defined - including the next topic, macros! -)", -R"(Enter ':okay!' into the prompt when you're ready to continue. -)", Value("okay!")}); - intro_sections.push({}); // Macros - add_step({ -R"(One of the biggest advantages of a simple, homoiconic +)", + R"(Enter ':okay!' into the prompt when you're ready to continue. +)", + Value("okay!")}); + intro_sections.push({}); // Macros + add_step({ + R"(One of the biggest advantages of a simple, homoiconic syntax is being able to support macros. In Basil, macros behave very similarly to variables and procedures, only operating on source code instead of values. The building block of all of this is the splice special form, which will evaluate a list of terms and insert the result in its place in the source code. -)", -R"(Enter 'quote (1 + (splice 1 + 1))' into the prompt. -)", list_of(1, Value("+"), 2), "Macros"}); - add_step({ -R"(As a bit of syntactic sugar, any terms enclosed in '|' +)", + R"(Enter 'quote (1 + (splice 1 + 1))' into the prompt. +)", + list_of(1, Value("+"), 2), "Macros"}); + add_step({ + R"(As a bit of syntactic sugar, any terms enclosed in '|' characters become a splice. -)", -R"(Enter 'quote (|2 * 2|)' into the prompt. -)", list_of(4)}); - add_step({ -R"(The simplest kind of macro is an alias, kind of like +)", + R"(Enter 'quote (|2 * 2|)' into the prompt. +)", + list_of(4)}); + add_step({ + R"(The simplest kind of macro is an alias, kind of like a macro variable. It'll replace itself wherever it's used with a Basil term. For example, let's say you prefer 'let' instead of 'def' to define variables: -)", -R"(Enter: +)", + R"(Enter: macro let def let x 1 x into the prompt. -)", Value(1)}); - add_step({ -R"(We can also define macro procedures. These work just +)", + Value(1)}); + add_step({ + R"(We can also define macro procedures. These work just like normal procedures, only they're defined with 'macro' instead of 'def', and they automatically quote their arguments and body. Instead of evaluating the body to get a value, macro procedures splice code into the body to generate the desired code. -)", -R"(Enter: +)", + R"(Enter: macro (dotwice expr) |expr| |expr| dotwice (display "hello") into the prompt. -)", runtime(VOID)}); - add_step({ -R"(Like normal procedures, macros can also be +)", + runtime(VOID)}); + add_step({ + R"(Like normal procedures, macros can also be declared infix. The 'infix-macro' special form is used which, like 'infix', can optionally be given a precedence. -)", -R"(Enter: +)", + R"(Enter: infix-macro (expr unless cond) if |cond| [] else |expr| (display "bad thing!") unless 1 == 1 into the prompt. -)", Value(VOID)}); - add_step({ -R"(Again like normal procedures, macros can be declared +)", + Value(VOID)}); + add_step({ + R"(Again like normal procedures, macros can be declared mixfix, with any number of keywords. This applies to both 'macro' and 'infix-macro'. -)", -R"(Enter: +)", + R"(Enter: macro (print expr :if cond) if |cond| display |expr| print "hello" if (3 > 2) into the prompt. -)", runtime(VOID)}); - add_step({ -R"(That's actually all there is to macros! They're not +)", + runtime(VOID)}); + add_step({ + R"(That's actually all there is to macros! They're not too special or complex compared to other functions, despite giving you direct control over the language's syntax. Like infix functions, this is one of the Basil features we're really proud of! -)", -R"(Enter ':okay!' into the prompt when you're ready to continue. -)", Value("okay!")}); - intro_sections.push({}); // Macros - add_step({ -R"(The last main feature of Basil is importing other +)", + R"(Enter ':okay!' into the prompt when you're ready to continue. +)", + Value("okay!")}); + intro_sections.push({}); // Macros + add_step({ + R"(The last main feature of Basil is importing other source files. This is done very simply, using the 'use' special form. 'use' takes one argument, a symbol, and loads the file at that path (with a '.bl' file extension appended to it). -)", -R"(Enter: +)", + R"(Enter: use std/list sort [4 2 3 1] into the prompt. -)", runtime(find(INT)), "Imports"}); - add_step({ -R"('use' will evaluate the file it's provided, but instead +)", + runtime(find(INT)), "Imports"}); + add_step({ + R"('use' will evaluate the file it's provided, but instead of producing any side-effects or values, it will simply take the definitions produced from the file and add them to the current environment. @@ -789,17 +833,18 @@ but you can take a look around the project directory and play around with some of the functions we've written! Or write your own files if you like!. )", -R"(Enter ':okay!' into the prompt when you're ready to continue. -)", Value("okay!")}); + R"(Enter ':okay!' into the prompt when you're ready to continue. +)", + Value("okay!")}); } #include "stdlib.h" #include "time.h" void print_intro_conclusion() { - println(BOLDGREEN, "Congratulations! \n", RESET); - println( -R"(Congratulations! You've completed the Basil + println(BOLDGREEN, "Congratulations! \n", RESET); + println( + R"(Congratulations! You've completed the Basil interactive tour! If you'd like to continue to play around with the @@ -819,81 +864,71 @@ Bye for now! } int intro() { - srand(time(0)); - init_intro_sections(); - - print_banner(); - println(BOLDGREEN, "Welcome to the interactive Basil tour!", RESET); - println(BOLDGREEN, "Press Enter twice after entering a command to submit it.", RESET); - - Source src; - - Builtin REPL_QUIT(find(find(), VOID), repl_quit, nullptr), - INTRO_HELP(find(find(), VOID), intro_help, nullptr), - INTRO_CONTENTS(find(find(), VOID), intro_contents, nullptr), - INTRO_START(find(find(), VOID), intro_start, nullptr), - INTRO_SET_SECTION(find(find(INT), VOID), intro_set_section, nullptr); - - ref root = create_root_env(); - root->def("$quit", Value(root, REPL_QUIT), 0); - root->def("$help", Value(root, INTRO_HELP), 0); - root->def("$contents", Value(root, INTRO_CONTENTS), 0); - root->def("$start", Value(root, INTRO_START), 0); - root->def("$section", Value(root, INTRO_SET_SECTION), 1); - - intro_help(root, Value(VOID)); - - const char* congratulations[] = { - "Awesome!", - "Great!", - "Nice!", - "Cool!", - "You did it!", - "You got it!" - }; - - while (!repl_done) { - ref global = newref(root); - Function main_fn("main"); - - bool is_running_intro = running_intro; - if (is_running_intro) { - const IntroStep& step = intro_sections[intro_section].steps[intro_section_index]; - if (step.title) - println(intro_section + 1, ". ", BOLDGREEN, step.title, RESET, '\n'); - println(BOLDWHITE, step.text, RESET); - println(ITALICWHITE, step.instruction, RESET); - } - - if (is_running_intro) { - bool correct = false; - - while (!correct && !repl_done) { - Value result = repl(global, src, main_fn); - - const IntroStep& step = intro_sections[intro_section].steps[intro_section_index]; - if (result.is_runtime() && step.expected.is_runtime()) - correct = result.type() == step.expected.type(); - else correct = result == step.expected; - - if (!correct) - println(ITALICRED, "Expected '", step.expected, "', but given '", - result, "'.", RESET, '\n'); - else { - println(ITALICWHITE, congratulations[rand() % 6], RESET); - intro_section_index ++; - if (intro_section_index >= intro_sections[intro_section].steps.size()) - intro_section ++, intro_section_index = 0; - println(""); - usleep(1000000); - println("⸻", "\n"); - if (intro_section >= intro_sections.size()) - repl_done = true, print_intro_conclusion(); - } - } - } - else repl(global, src, main_fn); - } - - return 0; + srand(time(0)); + init_intro_sections(); + + print_banner(); + println(BOLDGREEN, "Welcome to the interactive Basil tour!", RESET); + println(BOLDGREEN, "Press Enter twice after entering a command to submit it.", RESET); + + Source src; + + Builtin REPL_QUIT(find(find(), VOID), repl_quit, nullptr), + INTRO_HELP(find(find(), VOID), intro_help, nullptr), + INTRO_CONTENTS(find(find(), VOID), intro_contents, nullptr), + INTRO_START(find(find(), VOID), intro_start, nullptr), + INTRO_SET_SECTION(find(find(INT), VOID), intro_set_section, nullptr); + + ref root = create_root_env(); + root->def("$quit", Value(root, REPL_QUIT), 0); + root->def("$help", Value(root, INTRO_HELP), 0); + root->def("$contents", Value(root, INTRO_CONTENTS), 0); + root->def("$start", Value(root, INTRO_START), 0); + root->def("$section", Value(root, INTRO_SET_SECTION), 1); + + intro_help(root, Value(VOID)); + + const char* congratulations[] = {"Awesome!", "Great!", "Nice!", "Cool!", "You did it!", "You got it!"}; + + while (!repl_done) { + ref global = newref(root); + Function main_fn("main"); + + bool is_running_intro = running_intro; + if (is_running_intro) { + const IntroStep& step = intro_sections[intro_section].steps[intro_section_index]; + if (step.title) println(intro_section + 1, ". ", BOLDGREEN, step.title, RESET, '\n'); + println(BOLDWHITE, step.text, RESET); + println(ITALICWHITE, step.instruction, RESET); + } + + if (is_running_intro) { + bool correct = false; + + while (!correct && !repl_done) { + Value result = repl(global, src, main_fn); + + const IntroStep& step = intro_sections[intro_section].steps[intro_section_index]; + if (result.is_runtime() && step.expected.is_runtime()) correct = result.type() == step.expected.type(); + else + correct = result == step.expected; + + if (!correct) + println(ITALICRED, "Expected '", step.expected, "', but given '", result, "'.", RESET, '\n'); + else { + println(ITALICWHITE, congratulations[rand() % 6], RESET); + intro_section_index++; + if (intro_section_index >= intro_sections[intro_section].steps.size()) + intro_section++, intro_section_index = 0; + println(""); + usleep(1000000); + println("⸻", "\n"); + if (intro_section >= intro_sections.size()) repl_done = true, print_intro_conclusion(); + } + } + } else + repl(global, src, main_fn); + } + + return 0; } diff --git a/compiler/native.cpp b/compiler/native.cpp index 6cc300e..8e8b3f8 100644 --- a/compiler/native.cpp +++ b/compiler/native.cpp @@ -1,201 +1,206 @@ #include "native.h" -#include "values.h" -#include "util/io.h" #include "stdlib.h" +#include "util/io.h" +#include "values.h" namespace basil { - using namespace jasmine; - - void add_native_function(Object& object, const string& name, void* function) { - using namespace x64; - writeto(object); - Symbol sym = global((const char*)name.raw()); - label(sym); - mov(r64(RAX), imm(i64(function))); - call(r64(RAX)); - ret(); - } - - void* _cons(i64 value, void* next) { - void* result = malloc(sizeof(i64) + sizeof(void*)); - *(i64*)result = value; - *((void**)result + 1) = next; - return result; - } - - i64 _listlen(void* list) { - u32 size = 0; - while (list) { - list = *((void**)list + 1); - size ++; - } - return size; - } - - void _display_int(i64 value) { - println(value); - } - - void _display_symbol(u64 value) { - println(symbol_for(value)); - } - - void _display_bool(bool value) { - println(value); - } - - void _display_string(const char* value) { - println(value); - } - - template - void _display_list(void* value) { - print("("); - bool first = true; - while (value) { - T i = (T)*(u64*)value; - print(first ? "" : " ", i); - value = *((void**)value + 1); - first = false; - } - println(")"); - } - - void _display_int_list(void* value) { - print("("); - bool first = true; - while (value) { - i64 i = *(i64*)value; - print(first ? "" : " ", i); - value = *((void**)value + 1); - first = false; - } - println(")"); - } - - void _display_bool_list(void* value) { - print("("); - bool first = true; - while (value) { - u64 i = *(u64*)value; - print(first ? "" : " ", i != 0); - value = *((void**)value + 1); - first = false; - } - println(")"); - } - - void _display_symbol_list(void* value) { - print("("); - bool first = true; - while (value) { - u64 i = *(u64*)value; - print(first ? "" : " ", symbol_for(i)); - value = *((void**)value + 1); - first = false; - } - println(")"); - } - - void _display_native_string_list(void* value) { - print("("); - bool first = true; - while (value) { - const char* i = *(const char**)value; - print(first ? "\"" : " \"", i, '"'); - value = *((void**)value + 1); - first = false; - } - println(")"); - } - - void display_native_list(const Type* t, void* list) { - if (t->kind() != KIND_LIST) return; - const Type* elt = ((const ListType*)t)->element(); - if (elt == INT) _display_list(list); - else if (elt == SYMBOL) _display_symbol_list(list); - else if (elt == BOOL) _display_list(list); - else if (elt == VOID) _display_list(list); - else if (elt == STRING) _display_native_string_list(list); - } - - i64 _strcmp(const char *a, const char *b) { - while (*a && *b && *a == *b) a ++, b ++; - return *(const unsigned char*)a - *(const unsigned char*)b; - } - - i64 _strlen(const char *s) { - const char *copy = s; - while (*s++); - return s - copy - 1; - } - - const u8* _read_line() { - string s; - while (_stdin.peek() != '\n') { s += _stdin.read(); } - u8* buf = new u8[s.size() + 1]; - for (u32 i = 0; i < s.size(); i ++) buf[i] = s[i]; - buf[s.size()] = '\0'; - return buf; - } - - i64 _read_int() { - i64 i; - read(_stdin, i); - return i; - } - - const u8* _read_word() { - string s; - read(_stdin, s); - u8* buf = new u8[s.size() + 1]; - for (u32 i = 0; i < s.size(); i ++) buf[i] = s[i]; - buf[s.size()] = '\0'; - return buf; - } - - u8 _char_at(const char *s, i64 idx) { - return s[idx]; - } - - const u8* _strcat(const char* a, const char* b) { - u8* buf = new u8[_strlen(a) + _strlen(b) + 1]; - u32 i = 0; - for (;*a;a ++) buf[i ++] = *a; - for (;*b;b ++) buf[i ++] = *b; - buf[i] = '\0'; - return buf; - } - - const u8* _substr(const char *s, i64 start, i64 end) { - if (end < start) return new u8[1]{'\0'}; - u8* buf = new u8[end - start + 1]; - i64 i = start; - for (; i < end; i ++) buf[i - start] = s[i]; - buf[i + 1] = '\0'; - return buf; - } - - void add_native_functions(Object& object) { - add_native_function(object, "_cons", (void*)_cons); - - add_native_function(object, "_strcmp", (void*)_strcmp); - add_native_function(object, "_strlen", (void*)_strlen); - add_native_function(object, "_strcat", (void*)_strcat); - add_native_function(object, "_substr", (void*)_substr); - add_native_function(object, "_read_line", (void*)_read_line); - add_native_function(object, "_read_int", (void*)_read_int); - add_native_function(object, "_read_word", (void*)_read_word); - add_native_function(object, "_char_at", (void*)_char_at); - add_native_function(object, "_listlen", (void*)_listlen); - - add_native_function(object, "_display_int", (void*)_display_int); - add_native_function(object, "_display_symbol", (void*)_display_symbol); - add_native_function(object, "_display_bool", (void*)_display_bool); - add_native_function(object, "_display_string", (void*)_display_string); - add_native_function(object, "_display_int_list", (void*)_display_int_list); - add_native_function(object, "_display_symbol_list", (void*)_display_symbol_list); - add_native_function(object, "_display_bool_list", (void*)_display_bool_list); - add_native_function(object, "_display_string_list", (void*)_display_native_string_list); - } -} \ No newline at end of file + using namespace jasmine; + + void add_native_function(Object& object, const string& name, void* function) { + using namespace x64; + writeto(object); + Symbol sym = global((const char*)name.raw()); + label(sym); + mov(r64(RAX), imm(i64(function))); + call(r64(RAX)); + ret(); + } + + void* _cons(i64 value, void* next) { + void* result = malloc(sizeof(i64) + sizeof(void*)); + *(i64*)result = value; + *((void**)result + 1) = next; + return result; + } + + i64 _listlen(void* list) { + u32 size = 0; + while (list) { + list = *((void**)list + 1); + size++; + } + return size; + } + + void _display_int(i64 value) { + println(value); + } + + void _display_symbol(u64 value) { + println(symbol_for(value)); + } + + void _display_bool(bool value) { + println(value); + } + + void _display_string(const char* value) { + println(value); + } + + template + void _display_list(void* value) { + print("("); + bool first = true; + while (value) { + T i = (T) * (u64*)value; + print(first ? "" : " ", i); + value = *((void**)value + 1); + first = false; + } + println(")"); + } + + void _display_int_list(void* value) { + print("("); + bool first = true; + while (value) { + i64 i = *(i64*)value; + print(first ? "" : " ", i); + value = *((void**)value + 1); + first = false; + } + println(")"); + } + + void _display_bool_list(void* value) { + print("("); + bool first = true; + while (value) { + u64 i = *(u64*)value; + print(first ? "" : " ", i != 0); + value = *((void**)value + 1); + first = false; + } + println(")"); + } + + void _display_symbol_list(void* value) { + print("("); + bool first = true; + while (value) { + u64 i = *(u64*)value; + print(first ? "" : " ", symbol_for(i)); + value = *((void**)value + 1); + first = false; + } + println(")"); + } + + void _display_native_string_list(void* value) { + print("("); + bool first = true; + while (value) { + const char* i = *(const char**)value; + print(first ? "\"" : " \"", i, '"'); + value = *((void**)value + 1); + first = false; + } + println(")"); + } + + void display_native_list(const Type* t, void* list) { + if (t->kind() != KIND_LIST) return; + const Type* elt = ((const ListType*)t)->element(); + if (elt == INT) _display_list(list); + else if (elt == SYMBOL) + _display_symbol_list(list); + else if (elt == BOOL) + _display_list(list); + else if (elt == VOID) + _display_list(list); + else if (elt == STRING) + _display_native_string_list(list); + } + + i64 _strcmp(const char* a, const char* b) { + while (*a && *b && *a == *b) a++, b++; + return *(const unsigned char*)a - *(const unsigned char*)b; + } + + i64 _strlen(const char* s) { + const char* copy = s; + while (*s++) + ; + return s - copy - 1; + } + + const u8* _read_line() { + string s; + while (_stdin.peek() != '\n') { s += _stdin.read(); } + u8* buf = new u8[s.size() + 1]; + for (u32 i = 0; i < s.size(); i++) buf[i] = s[i]; + buf[s.size()] = '\0'; + return buf; + } + + i64 _read_int() { + i64 i; + read(_stdin, i); + return i; + } + + const u8* _read_word() { + string s; + read(_stdin, s); + u8* buf = new u8[s.size() + 1]; + for (u32 i = 0; i < s.size(); i++) buf[i] = s[i]; + buf[s.size()] = '\0'; + return buf; + } + + u8 _char_at(const char* s, i64 idx) { + return s[idx]; + } + + const u8* _strcat(const char* a, const char* b) { + u8* buf = new u8[_strlen(a) + _strlen(b) + 1]; + u32 i = 0; + for (; *a; a++) buf[i++] = *a; + for (; *b; b++) buf[i++] = *b; + buf[i] = '\0'; + return buf; + } + + const u8* _substr(const char* s, i64 start, i64 end) { + if (end < start) return new u8[1]{'\0'}; + u8* buf = new u8[end - start + 1]; + i64 i = start; + for (; i < end; i++) buf[i - start] = s[i]; + buf[i + 1] = '\0'; + return buf; + } + + void add_native_functions(Object& object) { + add_native_function(object, "_cons", (void*)_cons); + + add_native_function(object, "_strcmp", (void*)_strcmp); + add_native_function(object, "_strlen", (void*)_strlen); + add_native_function(object, "_strcat", (void*)_strcat); + add_native_function(object, "_substr", (void*)_substr); + add_native_function(object, "_read_line", (void*)_read_line); + add_native_function(object, "_read_int", (void*)_read_int); + add_native_function(object, "_read_word", (void*)_read_word); + add_native_function(object, "_char_at", (void*)_char_at); + add_native_function(object, "_listlen", (void*)_listlen); + + add_native_function(object, "_display_int", (void*)_display_int); + add_native_function(object, "_display_symbol", (void*)_display_symbol); + add_native_function(object, "_display_bool", (void*)_display_bool); + add_native_function(object, "_display_string", (void*)_display_string); + add_native_function(object, "_display_int_list", (void*)_display_int_list); + add_native_function(object, "_display_symbol_list", (void*)_display_symbol_list); + add_native_function(object, "_display_bool_list", (void*)_display_bool_list); + add_native_function(object, "_display_string_list", (void*)_display_native_string_list); + } +} // namespace basil \ No newline at end of file diff --git a/compiler/native.h b/compiler/native.h index af6c5ff..1a79a98 100644 --- a/compiler/native.h +++ b/compiler/native.h @@ -1,14 +1,14 @@ #ifndef BASIL_NATIVE_H #define BASIL_NATIVE_H -#include "util/defs.h" #include "ir.h" #include "jasmine/x64.h" +#include "util/defs.h" namespace basil { - void display_native_list(const Type* t, void* list); - void add_native_functions(jasmine::Object& object); - const u8* _read_line(); -} + void display_native_list(const Type* t, void* list); + void add_native_functions(jasmine::Object& object); + const u8* _read_line(); +} // namespace basil #endif \ No newline at end of file diff --git a/compiler/ops.cpp b/compiler/ops.cpp index 135db15..afd5881 100644 --- a/compiler/ops.cpp +++ b/compiler/ops.cpp @@ -17,11 +17,10 @@ namespace basil { switch (_arch) { case X86_64: { using namespace x64; - static u32 ARG_REGISTERS[] = { RDI, RSI, RCX, RDX, R8, R9 }; + static u32 ARG_REGISTERS[] = {RDI, RSI, RCX, RDX, R8, R9}; return ARG_REGISTERS[i]; } - default: - return 0; + default: return 0; } } @@ -29,11 +28,10 @@ namespace basil { switch (_arch) { case X86_64: { using namespace x64; - static u32 CLOBBER_REGISTERS[] = { RAX, RDX, RCX, RBX }; + static u32 CLOBBER_REGISTERS[] = {RAX, RDX, RCX, RBX}; return CLOBBER_REGISTERS[i]; } - default: - return 0; + default: return 0; } } @@ -41,16 +39,13 @@ namespace basil { switch (_arch) { case X86_64: { using namespace x64; - static Register ALLOCATABLE_REGISTERS[] = { - RBX, R8, R9, R10, R11, R12, R13, R14, R15 - }; + static Register ALLOCATABLE_REGISTERS[] = {RBX, R8, R9, R10, R11, R12, R13, R14, R15}; vector regs; - for (i64 i = sizeof(ALLOCATABLE_REGISTERS) / sizeof(Register) - 1; i >= 0; i --) + for (i64 i = sizeof(ALLOCATABLE_REGISTERS) / sizeof(Register) - 1; i >= 0; i--) regs.push(u32(ALLOCATABLE_REGISTERS[i])); return regs; } - default: - return {}; + default: return {}; } } @@ -65,19 +60,15 @@ namespace basil { x64::Arg x64_arg(const Location& src) { using namespace x64; switch (src.type) { - case LOC_NONE: - return imm64(0); - case LOC_REGISTER: - return r64((Register)src.reg); - case LOC_LABEL: - return label64(global((const char*)label_of(src).raw())); + case LOC_NONE: return imm64(0); + case LOC_REGISTER: return r64((Register)src.reg); + case LOC_LABEL: return label64(global((const char*)label_of(src).raw())); case LOC_LOCAL: if (local_of(src).reg >= 0) return r64((Register)local_of(src).reg); - else return m64(RBP, local_of(src).offset); - case LOC_CONSTANT: - return label64(global((const char*)constant_of(src).name.raw())); - case LOC_IMMEDIATE: - return imm64(src.immediate); + else + return m64(RBP, local_of(src).offset); + case LOC_CONSTANT: return label64(global((const char*)constant_of(src).name.raw())); + case LOC_IMMEDIATE: return imm64(src.immediate); } } @@ -86,9 +77,9 @@ namespace basil { if (is_memory(src.type)) { mov(clobber, src); return clobber; - } - else return src; - } + } else + return src; + } void x64_move(const x64::Arg& dest, const x64::Arg& src, const x64::Arg& clobber) { using namespace x64; @@ -96,19 +87,18 @@ namespace basil { if (is_memory(dest.type) && is_memory(src.type)) { mov(clobber, src); mov(dest, clobber); - } - else mov(dest, src); + } else + mov(dest, src); } - void x64_binary(const x64::Arg& dest, const x64::Arg& lhs, - const x64::Arg& rhs, const x64::Arg& clobber, void(*op)(const x64::Arg&, const x64::Arg&, x64::Size)) { + void x64_binary(const x64::Arg& dest, const x64::Arg& lhs, const x64::Arg& rhs, const x64::Arg& clobber, + void (*op)(const x64::Arg&, const x64::Arg&, x64::Size)) { using namespace x64; if (is_memory(dest.type) && (is_memory(lhs.type) || is_memory(rhs.type))) { x64_move(clobber, lhs, clobber); op(clobber, rhs, AUTO); x64_move(dest, clobber, clobber); - } - else { + } else { x64_move(dest, lhs, clobber); op(dest, rhs, AUTO); } @@ -119,8 +109,8 @@ namespace basil { if (is_memory(lhs.type) && is_memory(rhs.type)) { x64_move(clobber, lhs, clobber); cmp(clobber, rhs); - } - else cmp(lhs, rhs, AUTO); + } else + cmp(lhs, rhs, AUTO); } void store(const Location& dest, const Location& src, u32 offset) { @@ -128,13 +118,12 @@ namespace basil { case X86_64: { using namespace x64; auto d = x64_to_register(x64_arg(dest), x64_clobber(1)), - s = x64_to_register(x64_arg(src), x64_clobber(0)); - auto m = m64(d.data.reg, offset); - x64_move(m, s, x64_clobber(0)); + s = x64_to_register(x64_arg(src), x64_clobber(0)); + auto m = m64(d.data.reg, offset); + x64_move(m, s, x64_clobber(0)); return; } - default: - return; + default: return; } } @@ -142,14 +131,12 @@ namespace basil { switch (_arch) { case X86_64: { using namespace x64; - auto d = x64_arg(dest), - s = x64_to_register(x64_arg(src), x64_clobber(1)); - auto m = m64(s.data.reg, offset); - x64_move(d, m, x64_clobber(0)); + auto d = x64_arg(dest), s = x64_to_register(x64_arg(src), x64_clobber(1)); + auto m = m64(s.data.reg, offset); + x64_move(d, m, x64_clobber(0)); return; } - default: - return; + default: return; } } @@ -157,12 +144,11 @@ namespace basil { switch (_arch) { case X86_64: { using namespace x64; - auto d = x64_arg(dest), s = x64_arg(src); - x64_move(d, s, x64_clobber(0)); + auto d = x64_arg(dest), s = x64_arg(src); + x64_move(d, s, x64_clobber(0)); return; } - default: - return; + default: return; } } @@ -173,17 +159,17 @@ namespace basil { auto d = x64_arg(dest), l = x64_arg(lhs), r = x64_arg(rhs); if (is_register(d.type) && !is_memory(l.type) && !is_memory(r.type)) { if (d.data.reg == l.data.reg) // no need for two steps - if (is_immediate(r.type) && r.data.imm64 == 1) - return inc(d); + if (is_immediate(r.type) && r.data.imm64 == 1) return inc(d); else if (is_immediate(r.type) && r.data.imm64 == -1) return dec(d); - else return add(d, r); + else + return add(d, r); else if (d.data.reg == r.data.reg) // no need for two steps - if (is_immediate(l.type) && l.data.imm64 == 1) - return inc(d); + if (is_immediate(l.type) && l.data.imm64 == 1) return inc(d); else if (is_immediate(l.type) && l.data.imm64 == -1) return dec(d); - else return add(d, l); + else + return add(d, l); else if (is_register(l.type) && is_immediate(r.type)) return lea(d, m64(l.data.reg, r.data.imm64)); else if (is_immediate(l.type) && is_register(r.type)) @@ -191,11 +177,10 @@ namespace basil { else if (is_register(l.type) && is_register(r.type)) return lea(d, m64(l.data.reg, r.data.reg, SCALE1, 0)); } - x64_binary(x64_arg(dest), x64_arg(lhs), x64_arg(rhs), x64_clobber(0), x64::add); + x64_binary(x64_arg(dest), x64_arg(lhs), x64_arg(rhs), x64_clobber(0), x64::add); return; } - default: - return; + default: return; } } @@ -206,19 +191,18 @@ namespace basil { auto d = x64_arg(dest), l = x64_arg(lhs), r = x64_arg(rhs); if (is_register(d.type) && !is_memory(l.type) && !is_memory(r.type)) { if (d.data.reg == l.data.reg) // no need for two steps - if (is_immediate(r.type) && r.data.imm64 == 1) - return dec(d); + if (is_immediate(r.type) && r.data.imm64 == 1) return dec(d); else if (is_immediate(r.type) && r.data.imm64 == -1) return inc(d); - else return sub(d, r); + else + return sub(d, r); else if (is_register(l.type) && is_immediate(r.type)) return lea(d, m64(l.data.reg, -r.data.imm64)); } - x64_binary(x64_arg(dest), x64_arg(lhs), x64_arg(rhs), x64_clobber(0), x64::sub); + x64_binary(x64_arg(dest), x64_arg(lhs), x64_arg(rhs), x64_clobber(0), x64::sub); return; } - default: - return; + default: return; } } @@ -233,13 +217,12 @@ namespace basil { if (is_immediate(r.type)) { x64_move(x64_clobber(1), r, x64_clobber(1)); x64::imul(t, x64_clobber(1)); - } - else x64::imul(t, r); - if (is_memory(d.type)) mov(d, t); + } else + x64::imul(t, r); + if (is_memory(d.type)) mov(d, t); return; } - default: - return; + default: return; } } @@ -253,13 +236,12 @@ namespace basil { if (is_immediate(r.type)) { x64_move(x64_clobber(2), r, x64_clobber(2)); idiv(x64_clobber(2)); - } - else idiv(r); - x64_move(x64_arg(dest), x64_clobber(0), x64_clobber(0)); // dest <- rax + } else + idiv(r); + x64_move(x64_arg(dest), x64_clobber(0), x64_clobber(0)); // dest <- rax return; } - default: - return; + default: return; } } @@ -273,13 +255,12 @@ namespace basil { if (is_immediate(r.type)) { x64_move(x64_clobber(2), r, x64_clobber(2)); idiv(x64_clobber(2)); - } - else idiv(r); - x64_move(x64_arg(dest), x64_clobber(1), x64_clobber(0)); // dest <- rdx + } else + idiv(r); + x64_move(x64_arg(dest), x64_clobber(1), x64_clobber(0)); // dest <- rdx return; } - default: - return; + default: return; } } @@ -289,31 +270,22 @@ namespace basil { void and_op(const Location& dest, const Location& lhs, const Location& rhs) { switch (_arch) { - case X86_64: - x64_binary(x64_arg(dest), x64_arg(lhs), x64_arg(rhs), x64_clobber(0), x64::and_); - return; - default: - return; + case X86_64: x64_binary(x64_arg(dest), x64_arg(lhs), x64_arg(rhs), x64_clobber(0), x64::and_); return; + default: return; } } void or_op(const Location& dest, const Location& lhs, const Location& rhs) { switch (_arch) { - case X86_64: - x64_binary(x64_arg(dest), x64_arg(lhs), x64_arg(rhs), x64_clobber(0), x64::or_); - return; - default: - return; + case X86_64: x64_binary(x64_arg(dest), x64_arg(lhs), x64_arg(rhs), x64_clobber(0), x64::or_); return; + default: return; } } void xor_op(const Location& dest, const Location& lhs, const Location& rhs) { switch (_arch) { - case X86_64: - x64_binary(x64_arg(dest), x64_arg(lhs), x64_arg(rhs), x64_clobber(0), x64::xor_); - return; - default: - return; + case X86_64: x64_binary(x64_arg(dest), x64_arg(lhs), x64_arg(rhs), x64_clobber(0), x64::xor_); return; + default: return; } } @@ -321,84 +293,77 @@ namespace basil { switch (_arch) { case X86_64: { using namespace x64; - x64_binary(x64_arg(dest), x64_arg(src), imm64(0), x64_clobber(0), x64::cmp); + x64_binary(x64_arg(dest), x64_arg(src), imm64(0), x64_clobber(0), x64::cmp); setcc(x64_arg(dest), EQUAL); return; } - default: - return; + default: return; } } void equal(const Location& dest, const Location& lhs, const Location& rhs) { switch (_arch) { case X86_64: - x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); - x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); + x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); + x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); x64::setcc(x64_arg(dest), x64::EQUAL); return; - default: - return; + default: return; } } void not_equal(const Location& dest, const Location& lhs, const Location& rhs) { switch (_arch) { case X86_64: - x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); - x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); + x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); + x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); x64::setcc(x64_arg(dest), x64::NOT_EQUAL); return; - default: - return; + default: return; } } void less(const Location& dest, const Location& lhs, const Location& rhs) { switch (_arch) { case X86_64: - x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); - x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); + x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); + x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); x64::setcc(x64_arg(dest), x64::LESS); return; - default: - return; + default: return; } } void less_equal(const Location& dest, const Location& lhs, const Location& rhs) { switch (_arch) { case X86_64: - x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); - x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); + x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); + x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); x64::setcc(x64_arg(dest), x64::LESS_OR_EQUAL); return; - default: - return; + default: return; } } void greater(const Location& dest, const Location& lhs, const Location& rhs) { switch (_arch) { case X86_64: - x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); - x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); + x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); + x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); x64::setcc(x64_arg(dest), x64::GREATER); return; - default: - return; + default: return; } } void greater_equal(const Location& dest, const Location& lhs, const Location& rhs) { switch (_arch) { case X86_64: - x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); - x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); + x64_compare(x64_arg(lhs), x64_arg(rhs), x64_clobber(0)); + x64_move(x64_arg(dest), x64::imm64(0), x64_clobber(0)); x64::setcc(x64_arg(dest), x64::GREATER_OR_EQUAL); return; - default: - return; + default: return; } } @@ -410,22 +375,18 @@ namespace basil { if (is_memory(d.type)) { lea(x64_clobber(0), x64_arg(src)); x64_move(d, x64_clobber(0), x64_clobber(0)); - } - else lea(d, x64_arg(src)); + } else + lea(d, x64_arg(src)); return; } - default: - return; + default: return; } } void jump(const Location& dest) { switch (_arch) { - case X86_64: - x64::jmp(x64_arg(dest)); - return; - default: - return; + case X86_64: x64::jmp(x64_arg(dest)); return; + default: return; } } @@ -435,38 +396,28 @@ namespace basil { x64_compare(x64_arg(cond), x64::imm64(0), x64_clobber(0)); x64::jcc(x64_arg(dest), x64::EQUAL); return; - default: - return; + default: return; } } void set_arg(u32 i, const Location& src) { switch (_arch) { - case X86_64: - x64_move(x64_param(i), x64_arg(src), x64_clobber(0)); - return; - default: - return; + case X86_64: x64_move(x64_param(i), x64_arg(src), x64_clobber(0)); return; + default: return; } } void get_arg(const Location& dest, u32 i) { switch (_arch) { - case X86_64: - x64_move(x64_arg(dest), x64_param(i), x64_clobber(0)); - return; - default: - return; + case X86_64: x64_move(x64_arg(dest), x64_param(i), x64_clobber(0)); return; + default: return; } } void call(const Location& func) { switch (_arch) { - case X86_64: - x64::call(x64_arg(func)); - return; - default: - return; + case X86_64: x64::call(x64_arg(func)); return; + default: return; } } @@ -476,48 +427,35 @@ namespace basil { x64::call(x64_arg(func)); x64_move(x64_arg(dest), x64_clobber(0), x64_clobber(0)); return; - default: - return; + default: return; } } void global_label(const string& name) { switch (_arch) { - case X86_64: - x64::label(jasmine::global((const char*)name.raw())); - return; - default: - return; + case X86_64: x64::label(jasmine::global((const char*)name.raw())); return; + default: return; } } void local_label(const string& name) { switch (_arch) { - case X86_64: - x64::label(jasmine::local((const char*)name.raw())); - return; - default: - return; + case X86_64: x64::label(jasmine::local((const char*)name.raw())); return; + default: return; } } void push(const Location& src) { switch (_arch) { - case X86_64: - x64::push(x64_arg(src)); - return; - default: - return; + case X86_64: x64::push(x64_arg(src)); return; + default: return; } } void pop(const Location& dest) { switch (_arch) { - case X86_64: - x64::pop(x64_arg(dest)); - return; - default: - return; + case X86_64: x64::pop(x64_arg(dest)); return; + default: return; } } @@ -532,8 +470,7 @@ namespace basil { } return; } - default: - return; + default: return; } } @@ -548,8 +485,7 @@ namespace basil { ret(); return; } - default: - return; + default: return; } } -} \ No newline at end of file +} // namespace basil \ No newline at end of file diff --git a/compiler/ops.h b/compiler/ops.h index 2e2db3d..187af1f 100644 --- a/compiler/ops.h +++ b/compiler/ops.h @@ -1,10 +1,10 @@ #ifndef BASIL_OPS_H #define BASIL_OPS_H -#include "util/defs.h" -#include "util/vec.h" #include "ir.h" #include "jasmine/target.h" +#include "util/defs.h" +#include "util/vec.h" namespace basil { Architecture arch(); @@ -46,6 +46,6 @@ namespace basil { void pop(const Location& dest); void open_frame(u32 size); void close_frame(u32 size); -} +} // namespace basil #endif \ No newline at end of file diff --git a/compiler/parse.h b/compiler/parse.h index b303ecb..6c1e210 100644 --- a/compiler/parse.h +++ b/compiler/parse.h @@ -1,15 +1,14 @@ #ifndef BASIL_PARSE_H #define BASIL_PARSE_H -#include "util/defs.h" #include "lex.h" +#include "util/defs.h" #include "util/vec.h" #include "values.h" namespace basil { - Value parse(TokenView& view, u32 indent); - Value parse_line(TokenView& view, u32 indent, - bool consume_line = true); -} + Value parse(TokenView& view, u32 indent); + Value parse_line(TokenView& view, u32 indent, bool consume_line = true); +} // namespace basil #endif \ No newline at end of file diff --git a/compiler/source.cpp b/compiler/source.cpp index acd319d..cf7f3b3 100644 --- a/compiler/source.cpp +++ b/compiler/source.cpp @@ -2,131 +2,125 @@ #include "util/io.h" namespace basil { - void Source::add_lines(string* s) { - if ((*s)[s->size() - 1] != '\n') *s += '\n'; - const u8* p = s->raw(); - const u8* start = p; - u32 size = 0; - while (*p) { - size ++; // we're not at a null char, so this char should be included - if (*p == '\n') { - _lines.push({ size, start }); - start = p + 1; // next line starts after the \n - size = 0; - } - p ++; - } - if (size > 0) _lines.push({ size, start }); // in case file doesn't end with a newline - } - - void Source::calc_lines() { - _lines.clear(); - - for (string* s : _sections) { - add_lines(s); - } - } - - Source::Source() {} - - Source::Source(const char* filename) { - file f(filename, "r"); - if (!f) return; - - _sections.push(new string()); - while (f.peek()) { - if (f.peek() == '\t') *_sections.back() += " ", f.read(); - else *_sections.back() += f.read(); - } - - calc_lines(); - } - - Source::~Source() { - for (string * s : _sections) delete s; - } - - Source::Source(const Source& other) { - for (string *s : _sections) delete s; - _sections.clear(); - for (string *s : other._sections) { - _sections.push(new string(*s)); - } - calc_lines(); - } - - Source& Source::operator=(const Source& other) { - if (this == &other) return *this; - for (string *s : _sections) delete s; - _sections.clear(); - for (string *s : other._sections) { - _sections.push(new string(*s)); - } - calc_lines(); - return *this; - } - - void Source::add_line(const string& text) { - string* line = new string(text); - add_lines(line); - } - - const_slice Source::line(u32 i) const { - return _lines[i]; - } - - const vector>& Source::lines() const { - return _lines; - } - - Source::View::View(const Source* const src, u32 line, u32 column): - _src(src), _line(line), _column(column) {} - - u8 Source::View::peek() const { - if (_line >= _src->lines().size()) return '\0'; - return _src->line(_line)[_column]; - } - - u8 Source::View::read() { - u8 ch = peek(); - if (!ch) return ch; // exit early on null char - - _column ++; - if (_column >= _src->line(_line).size()) - _column = 0, _line ++; - - return ch; - } - - const u8* Source::View::pos() const { - return &_src->line(_line)[_column]; - } - - u32 Source::View::col() const { - return _column; - } - - u32 Source::View::line() const { - return _line; - } - - Source::View Source::begin() const { - return Source::View(this, 0, 0); - } + void Source::add_lines(string* s) { + if ((*s)[s->size() - 1] != '\n') *s += '\n'; + const u8* p = s->raw(); + const u8* start = p; + u32 size = 0; + while (*p) { + size++; // we're not at a null char, so this char should be included + if (*p == '\n') { + _lines.push({size, start}); + start = p + 1; // next line starts after the \n + size = 0; + } + p++; + } + if (size > 0) _lines.push({size, start}); // in case file doesn't end with a newline + } + + void Source::calc_lines() { + _lines.clear(); + + for (string* s : _sections) { add_lines(s); } + } + + Source::Source() {} - Source::View Source::expand(stream& io, u8 last) { - Source::View view = Source::View(this, _lines.size(), 0); + Source::Source(const char* filename) { + file f(filename, "r"); + if (!f) return; + + _sections.push(new string()); + while (f.peek()) { + if (f.peek() == '\t') *_sections.back() += " ", f.read(); + else + *_sections.back() += f.read(); + } + + calc_lines(); + } + + Source::~Source() { + for (string* s : _sections) delete s; + } - string* line = new string(); - while (io.peek() != last) { - if (io.peek() == '\t') *line += " ", io.read(); - else *line += io.read(); + Source::Source(const Source& other) { + for (string* s : _sections) delete s; + _sections.clear(); + for (string* s : other._sections) { _sections.push(new string(*s)); } + calc_lines(); } - *line += io.read(); - add_lines(line); - _sections.push(line); - - return view; - } -} \ No newline at end of file + Source& Source::operator=(const Source& other) { + if (this == &other) return *this; + for (string* s : _sections) delete s; + _sections.clear(); + for (string* s : other._sections) { _sections.push(new string(*s)); } + calc_lines(); + return *this; + } + + void Source::add_line(const string& text) { + string* line = new string(text); + add_lines(line); + } + + const_slice Source::line(u32 i) const { + return _lines[i]; + } + + const vector>& Source::lines() const { + return _lines; + } + + Source::View::View(const Source* const src, u32 line, u32 column) : _src(src), _line(line), _column(column) {} + + u8 Source::View::peek() const { + if (_line >= _src->lines().size()) return '\0'; + return _src->line(_line)[_column]; + } + + u8 Source::View::read() { + u8 ch = peek(); + if (!ch) return ch; // exit early on null char + + _column++; + if (_column >= _src->line(_line).size()) _column = 0, _line++; + + return ch; + } + + const u8* Source::View::pos() const { + return &_src->line(_line)[_column]; + } + + u32 Source::View::col() const { + return _column; + } + + u32 Source::View::line() const { + return _line; + } + + Source::View Source::begin() const { + return Source::View(this, 0, 0); + } + + Source::View Source::expand(stream& io, u8 last) { + Source::View view = Source::View(this, _lines.size(), 0); + + string* line = new string(); + while (io.peek() != last) { + if (io.peek() == '\t') *line += " ", io.read(); + else + *line += io.read(); + } + *line += io.read(); + + add_lines(line); + _sections.push(line); + + return view; + } +} // namespace basil \ No newline at end of file diff --git a/compiler/source.h b/compiler/source.h index eb0854a..d5b56b4 100644 --- a/compiler/source.h +++ b/compiler/source.h @@ -2,44 +2,46 @@ #define BASIL_SOURCE_H #include "util/defs.h" -#include "util/vec.h" #include "util/str.h" +#include "util/vec.h" namespace basil { - class Source { - vector _sections; - vector> _lines; - - void add_lines(string* s); - void calc_lines(); - public: - Source(); - Source(const char* filename); - ~Source(); - Source(const Source& other); - Source& operator=(const Source& other); - - void add_line(const string& text); - const_slice line(u32 i) const; - const vector>& lines() const; - - class View { - const Source* const _src; - u32 _line, _column; - - View(const Source* const src, u32 line, u32 column); - friend class Source; - public: - u8 peek() const; - u8 read(); - u32 line() const; - u32 col() const; - const u8* pos() const; - }; + class Source { + vector _sections; + vector> _lines; + + void add_lines(string* s); + void calc_lines(); + + public: + Source(); + Source(const char* filename); + ~Source(); + Source(const Source& other); + Source& operator=(const Source& other); - View begin() const; - View expand(stream& io, u8 last = '\n'); - }; -} + void add_line(const string& text); + const_slice line(u32 i) const; + const vector>& lines() const; + + class View { + const Source* const _src; + u32 _line, _column; + + View(const Source* const src, u32 line, u32 column); + friend class Source; + + public: + u8 peek() const; + u8 read(); + u32 line() const; + u32 col() const; + const u8* pos() const; + }; + + View begin() const; + View expand(stream& io, u8 last = '\n'); + }; +} // namespace basil -#endif \ No newline at end of file +#endif \ No newline at end of file diff --git a/compiler/ssa.cpp b/compiler/ssa.cpp index ec547e0..656a21a 100644 --- a/compiler/ssa.cpp +++ b/compiler/ssa.cpp @@ -14,19 +14,18 @@ namespace basil { id_names.push(name); id_counts.push(0); // start new ident chain for name - return { id_names.size() - 1, id_counts.back() }; - } - else { + return {id_names.size() - 1, id_counts.back()}; + } else { // find next ident in chain, incrementing count - return { it->second, ++ id_counts[it->second] }; + return {it->second, ++id_counts[it->second]}; } } const u8 MAJOR_MASK = 0b11100000; const u8 MINOR_MASK = 0b00011000; - SSANode::SSANode(ref parent, const Type* type, SSAKind kind, const string& name): - _parent(parent), _type(type), _kind(kind), _id(next_ident(name)) {} + SSANode::SSANode(ref parent, const Type* type, SSAKind kind, const string& name) + : _parent(parent), _type(type), _kind(kind), _id(next_ident(name)) {} bool SSANode::is(SSAKind kind) const { return _kind == kind; @@ -54,10 +53,10 @@ namespace basil { static u64 bb_count = 0; string next_bb() { - return format(".BB", bb_count ++); + return format(".BB", bb_count++); } - - BasicBlock::BasicBlock(const string& label): _label(label) { + + BasicBlock::BasicBlock(const string& label) : _label(label) { if (!_label.size()) _label = next_bb(); } @@ -105,12 +104,10 @@ namespace basil { void BasicBlock::format(stream& io) const { writeln(io, label(), ":"); - for (auto& member : _members) - writeln(io, " ", member); + for (auto& member : _members) writeln(io, " ", member); } - SSAInt::SSAInt(ref parent, i64 value): - SSANode(parent, INT, SSA_INT), _value(value) {} + SSAInt::SSAInt(ref parent, i64 value) : SSANode(parent, INT, SSA_INT), _value(value) {} i64 SSAInt::value() const { return _value; @@ -128,8 +125,7 @@ namespace basil { write(io, id(), " = ", _value); } - SSABool::SSABool(ref parent, bool value): - SSANode(parent, BOOL, SSA_BOOL), _value(value) {} + SSABool::SSABool(ref parent, bool value) : SSANode(parent, BOOL, SSA_BOOL), _value(value) {} bool SSABool::value() const { return _value; @@ -147,8 +143,8 @@ namespace basil { write(io, id(), " = ", _value); } - SSAString::SSAString(ref parent, const string& value): - SSANode(parent, STRING, SSA_STRING), _value(value) {} + SSAString::SSAString(ref parent, const string& value) + : SSANode(parent, STRING, SSA_STRING), _value(value) {} const string& SSAString::value() const { return _value; @@ -166,7 +162,7 @@ namespace basil { write(io, id(), " = \"", _value, "\""); } - SSAVoid::SSAVoid(ref parent): SSANode(parent, VOID, SSA_VOID) {} + SSAVoid::SSAVoid(ref parent) : SSANode(parent, VOID, SSA_VOID) {} Location SSAVoid::emit(Function& fn) { return loc_immediate(0); @@ -176,8 +172,7 @@ namespace basil { write(io, id(), " = []"); } - SSASymbol::SSASymbol(ref parent, u64 value): - SSANode(parent, SYMBOL, SSA_SYMBOL), _value(value) {} + SSASymbol::SSASymbol(ref parent, u64 value) : SSANode(parent, SYMBOL, SSA_SYMBOL), _value(value) {} u64 SSASymbol::value() const { return _value; @@ -193,10 +188,10 @@ namespace basil { void SSASymbol::format(stream& io) const { write(io, id(), " = ", symbol_for(_value)); - } - - SSAFunction::SSAFunction(ref parent, const Type* type, u64 name): - SSANode(parent, type, SSA_FUNCTION, symbol_for(name)), _name(name) {} + } + + SSAFunction::SSAFunction(ref parent, const Type* type, u64 name) + : SSANode(parent, type, SSA_FUNCTION, symbol_for(name)), _name(name) {} Location SSAFunction::emit(Function& fn) { return loc_label(symbol_for(_name)); @@ -206,8 +201,8 @@ namespace basil { write(io, id(), " = &", symbol_for(_name)); } - SSALoadLabel::SSALoadLabel(ref parent, const Type* type, u64 name): - SSANode(parent, type, SSA_LOAD_LABEL, symbol_for(name)), _name(name) {} + SSALoadLabel::SSALoadLabel(ref parent, const Type* type, u64 name) + : SSANode(parent, type, SSA_LOAD_LABEL, symbol_for(name)), _name(name) {} Location SSALoadLabel::emit(Function& fn) { return loc_label(symbol_for(_name)); @@ -217,15 +212,15 @@ namespace basil { write(io, id(), " = &", symbol_for(_name)); } - SSAStore::SSAStore(ref parent, ref env, u64 name, ref value): - SSANode(parent, value->type(), SSA_STORE, symbol_for(name)), _env(env), _value(value) {} + SSAStore::SSAStore(ref parent, ref env, u64 name, ref value) + : SSANode(parent, value->type(), SSA_STORE, symbol_for(name)), _env(env), _value(value) {} Location SSAStore::emit(Function& fn) { const string& name = id_names[id().base]; auto def = _env->find(name); if (!def) return loc_none(); - if (def->location.type == LOC_NONE) return fn.add(new StoreInsn(def->location = - fn.create_local(name, basil::INT), _value->emit(fn))); + if (def->location.type == LOC_NONE) + return fn.add(new StoreInsn(def->location = fn.create_local(name, basil::INT), _value->emit(fn))); return fn.add(new StoreInsn(def->location, _value->emit(fn))); } @@ -233,8 +228,8 @@ namespace basil { write(io, id(), " = ", _value->id()); } - SSABinary::SSABinary(ref parent, const Type* type, SSAKind kind, ref left, ref right): - SSANode(parent, type, kind), _left(left), _right(right) {} + SSABinary::SSABinary(ref parent, const Type* type, SSAKind kind, ref left, ref right) + : SSANode(parent, type, kind), _left(left), _right(right) {} ref SSABinary::left() const { return _left; @@ -254,42 +249,27 @@ namespace basil { Location SSABinary::emit(Function& fn) { switch (kind()) { - case SSA_ADD: - return fn.add(new AddInsn(_left->emit(fn), _right->emit(fn))); - case SSA_SUB: - return fn.add(new SubInsn(_left->emit(fn), _right->emit(fn))); - case SSA_MUL: - return fn.add(new MulInsn(_left->emit(fn), _right->emit(fn))); - case SSA_DIV: - return fn.add(new DivInsn(_left->emit(fn), _right->emit(fn))); - case SSA_REM: - return fn.add(new RemInsn(_left->emit(fn), _right->emit(fn))); - case SSA_AND: - return fn.add(new AndInsn(_left->emit(fn), _right->emit(fn))); - case SSA_OR: - return fn.add(new OrInsn(_left->emit(fn), _right->emit(fn))); - case SSA_XOR: - return fn.add(new XorInsn(_left->emit(fn), _right->emit(fn))); - case SSA_EQ: - return fn.add(new EqualInsn(_left->emit(fn), _right->emit(fn))); - case SSA_NOT_EQ: - return fn.add(new InequalInsn(_left->emit(fn), _right->emit(fn))); - case SSA_GREATER: - return fn.add(new GreaterInsn(_left->emit(fn), _right->emit(fn))); - case SSA_GREATER_EQ: - return fn.add(new GreaterEqualInsn(_left->emit(fn), _right->emit(fn))); - case SSA_LESS: - return fn.add(new LessInsn(_left->emit(fn), _right->emit(fn))); - case SSA_LESS_EQ: - return fn.add(new LessEqualInsn(_left->emit(fn), _right->emit(fn))); - default: - return loc_none(); + case SSA_ADD: return fn.add(new AddInsn(_left->emit(fn), _right->emit(fn))); + case SSA_SUB: return fn.add(new SubInsn(_left->emit(fn), _right->emit(fn))); + case SSA_MUL: return fn.add(new MulInsn(_left->emit(fn), _right->emit(fn))); + case SSA_DIV: return fn.add(new DivInsn(_left->emit(fn), _right->emit(fn))); + case SSA_REM: return fn.add(new RemInsn(_left->emit(fn), _right->emit(fn))); + case SSA_AND: return fn.add(new AndInsn(_left->emit(fn), _right->emit(fn))); + case SSA_OR: return fn.add(new OrInsn(_left->emit(fn), _right->emit(fn))); + case SSA_XOR: return fn.add(new XorInsn(_left->emit(fn), _right->emit(fn))); + case SSA_EQ: return fn.add(new EqualInsn(_left->emit(fn), _right->emit(fn))); + case SSA_NOT_EQ: return fn.add(new InequalInsn(_left->emit(fn), _right->emit(fn))); + case SSA_GREATER: return fn.add(new GreaterInsn(_left->emit(fn), _right->emit(fn))); + case SSA_GREATER_EQ: return fn.add(new GreaterEqualInsn(_left->emit(fn), _right->emit(fn))); + case SSA_LESS: return fn.add(new LessInsn(_left->emit(fn), _right->emit(fn))); + case SSA_LESS_EQ: return fn.add(new LessEqualInsn(_left->emit(fn), _right->emit(fn))); + default: return loc_none(); } } void SSABinary::format(stream& io) const { const char* op; - switch(kind()) { + switch (kind()) { case SSA_ADD: op = "+"; break; case SSA_SUB: op = "-"; break; case SSA_MUL: op = "*"; break; @@ -309,8 +289,8 @@ namespace basil { write(io, id(), " = ", _left->id(), " ", op, " ", _right->id()); } - SSAUnary::SSAUnary(ref parent, const Type* type, SSAKind kind, ref child): - SSANode(parent, type, kind), _child(child) {} + SSAUnary::SSAUnary(ref parent, const Type* type, SSAKind kind, ref child) + : SSANode(parent, type, kind), _child(child) {} ref SSAUnary::child() const { return _child; @@ -322,24 +302,22 @@ namespace basil { Location SSAUnary::emit(Function& fn) { switch (kind()) { - case SSA_NOT: - return fn.add(new NotInsn(_child->emit(fn))); - default: - return loc_none(); + case SSA_NOT: return fn.add(new NotInsn(_child->emit(fn))); + default: return loc_none(); } } void SSAUnary::format(stream& io) const { const char* op; - switch(kind()) { + switch (kind()) { case SSA_NOT: op = "not"; break; default: op = "?"; break; } write(io, id(), " = ", op, " ", _child->id()); } - SSACall::SSACall(ref parent, const Type* type, ref fn, const vector>& args): - SSANode(parent, type, SSA_CALL), _fn(fn), _args(args) {} + SSACall::SSACall(ref parent, const Type* type, ref fn, const vector>& args) + : SSANode(parent, type, SSA_CALL), _fn(fn), _args(args) {} ref SSACall::fn() const { return _fn; @@ -361,10 +339,8 @@ namespace basil { Location func = _fn->emit(fn); vector arglocs; for (auto node : _args) arglocs.push(node->emit(fn)); - for (u32 i = 0; i < _args.size(); i ++) { - if (arglocs[i].type == LOC_LABEL) { - arglocs[i] = fn.add(new AddressInsn(arglocs[i], _args[i]->type())); - } + for (u32 i = 0; i < _args.size(); i++) { + if (arglocs[i].type == LOC_LABEL) { arglocs[i] = fn.add(new AddressInsn(arglocs[i], _args[i]->type())); } } return fn.add(new CallInsn(func, arglocs, type())); } @@ -379,8 +355,7 @@ namespace basil { write(io, ")"); } - SSARet::SSARet(ref parent, ref value): - SSANode(parent, VOID, SSA_RET), _value(value) {} + SSARet::SSARet(ref parent, ref value) : SSANode(parent, VOID, SSA_RET), _value(value) {} ref SSARet::value() const { return _value; @@ -388,7 +363,7 @@ namespace basil { ref& SSARet::value() { return _value; - } + } Location SSARet::emit(Function& fn) { return fn.add(new RetInsn(_value->emit(fn))); @@ -398,8 +373,8 @@ namespace basil { write(io, "return ", _value->id()); } - SSAGoto::SSAGoto(ref parent, ref target): - SSANode(parent, VOID, SSA_GOTO), _target(target) {} + SSAGoto::SSAGoto(ref parent, ref target) + : SSANode(parent, VOID, SSA_GOTO), _target(target) {} ref SSAGoto::target() const { return _target; @@ -417,8 +392,8 @@ namespace basil { write(io, "goto ", _target->label()); } - SSAIf::SSAIf(ref parent, ref cond, ref if_true, ref if_false): - SSANode(parent, VOID, SSA_IF), _cond(cond), _if_true(if_true), _if_false(if_false) {} + SSAIf::SSAIf(ref parent, ref cond, ref if_true, ref if_false) + : SSANode(parent, VOID, SSA_IF), _cond(cond), _if_true(if_true), _if_false(if_false) {} ref SSAIf::cond() const { return _cond; @@ -453,9 +428,9 @@ namespace basil { } SSAPhi::SSAPhi(ref parent, const Type* type, ref left, ref right, - ref left_block, ref right_block): - SSANode(parent, type, SSA_PHI), _left(left), _right(right), - _left_block(left_block), _right_block(right_block) {} + ref left_block, ref right_block) + : SSANode(parent, type, SSA_PHI), _left(left), _right(right), _left_block(left_block), + _right_block(right_block) {} ref SSAPhi::left() const { return _left; @@ -472,7 +447,7 @@ namespace basil { ref& SSAPhi::right() { return _right; } - + ref SSAPhi::left_block() const { return _left_block; } @@ -495,8 +470,8 @@ namespace basil { void SSAPhi::format(stream& io) const { write(io, id(), " = phi ", _left, "(", _left_block->label(), ") ", _right, "(", _right_block->label(), ")"); - } -} + } +} // namespace basil void write(stream& io, const basil::SSAIdent& id) { write(io, basil::id_names[id.base], '#', id.count); diff --git a/compiler/ssa.h b/compiler/ssa.h index c2e20e5..252586c 100644 --- a/compiler/ssa.h +++ b/compiler/ssa.h @@ -1,14 +1,14 @@ #ifndef BASIL_SSA_H #define BASIL_SSA_H +#include "ir.h" #include "util/defs.h" -#include "util/vec.h" #include "util/rc.h" -#include "ir.h" +#include "util/vec.h" namespace basil { - class Def; - class Env; + class Def; + class Env; class BasicBlock; @@ -20,44 +20,44 @@ namespace basil { // 000 00 000 enum SSAKind : u8 { SSA_BINARY = 0, - SSA_BINARY_MATH = SSA_BINARY, - SSA_ADD = SSA_BINARY_MATH, - SSA_SUB = SSA_BINARY_MATH + 1, - SSA_MUL = SSA_BINARY_MATH + 2, - SSA_DIV = SSA_BINARY_MATH + 3, - SSA_REM = SSA_BINARY_MATH + 4, - SSA_BINARY_LOGIC = SSA_BINARY + 8, - SSA_AND = SSA_BINARY_LOGIC, - SSA_OR = SSA_BINARY_LOGIC + 1, - SSA_XOR = SSA_BINARY_LOGIC + 2, - SSA_BINARY_COMPARE = SSA_BINARY + 16, - SSA_EQ = SSA_BINARY_COMPARE, - SSA_NOT_EQ = SSA_BINARY_COMPARE + 1, - SSA_LESS = SSA_BINARY_COMPARE + 2, - SSA_LESS_EQ = SSA_BINARY_COMPARE + 3, - SSA_GREATER = SSA_BINARY_COMPARE + 4, - SSA_GREATER_EQ = SSA_BINARY_COMPARE + 5, + SSA_BINARY_MATH = SSA_BINARY, + SSA_ADD = SSA_BINARY_MATH, + SSA_SUB = SSA_BINARY_MATH + 1, + SSA_MUL = SSA_BINARY_MATH + 2, + SSA_DIV = SSA_BINARY_MATH + 3, + SSA_REM = SSA_BINARY_MATH + 4, + SSA_BINARY_LOGIC = SSA_BINARY + 8, + SSA_AND = SSA_BINARY_LOGIC, + SSA_OR = SSA_BINARY_LOGIC + 1, + SSA_XOR = SSA_BINARY_LOGIC + 2, + SSA_BINARY_COMPARE = SSA_BINARY + 16, + SSA_EQ = SSA_BINARY_COMPARE, + SSA_NOT_EQ = SSA_BINARY_COMPARE + 1, + SSA_LESS = SSA_BINARY_COMPARE + 2, + SSA_LESS_EQ = SSA_BINARY_COMPARE + 3, + SSA_GREATER = SSA_BINARY_COMPARE + 4, + SSA_GREATER_EQ = SSA_BINARY_COMPARE + 5, SSA_UNARY = 32, - SSA_UNARY_MATH = SSA_UNARY, - SSA_UNARY_LOGIC = SSA_UNARY + 8, - SSA_NOT = SSA_UNARY_LOGIC, + SSA_UNARY_MATH = SSA_UNARY, + SSA_UNARY_LOGIC = SSA_UNARY + 8, + SSA_NOT = SSA_UNARY_LOGIC, SSA_CONSTANT = 64, - SSA_INT = SSA_CONSTANT, - SSA_BOOL = SSA_CONSTANT + 1, - SSA_STRING = SSA_CONSTANT + 2, - SSA_VOID = SSA_CONSTANT + 3, - SSA_SYMBOL = SSA_CONSTANT + 4, - SSA_FUNCTION = SSA_CONSTANT + 5, + SSA_INT = SSA_CONSTANT, + SSA_BOOL = SSA_CONSTANT + 1, + SSA_STRING = SSA_CONSTANT + 2, + SSA_VOID = SSA_CONSTANT + 3, + SSA_SYMBOL = SSA_CONSTANT + 4, + SSA_FUNCTION = SSA_CONSTANT + 5, SSA_OTHER = 96, - SSA_LOAD = SSA_OTHER, - SSA_STORE = SSA_OTHER + 1, - SSA_CALL = SSA_OTHER + 2, - SSA_RET = SSA_OTHER + 3, - SSA_IF = SSA_OTHER + 4, - SSA_GOTO = SSA_OTHER + 5, - SSA_PHI = SSA_OTHER + 6, - SSA_BB = SSA_OTHER + 7, - SSA_LOAD_LABEL = SSA_OTHER + 8, + SSA_LOAD = SSA_OTHER, + SSA_STORE = SSA_OTHER + 1, + SSA_CALL = SSA_OTHER + 2, + SSA_RET = SSA_OTHER + 3, + SSA_IF = SSA_OTHER + 4, + SSA_GOTO = SSA_OTHER + 5, + SSA_PHI = SSA_OTHER + 6, + SSA_BB = SSA_OTHER + 7, + SSA_LOAD_LABEL = SSA_OTHER + 8, }; extern const u8 MAJOR_MASK; @@ -68,7 +68,8 @@ namespace basil { const Type* _type; SSAKind _kind; SSAIdent _id; - public: + + public: SSANode(ref parent, const Type* type, SSAKind kind, const string& name = "."); bool is(SSAKind kind) const; @@ -80,12 +81,13 @@ namespace basil { virtual Location emit(Function& fn) = 0; virtual void format(stream& io) const = 0; }; - + class BasicBlock { string _label; vector> _members; vector> _pred, _succ; - public: + + public: BasicBlock(const string& label = ""); const string& label() const; @@ -103,7 +105,8 @@ namespace basil { class SSAInt : public SSANode { i64 _value; - public: + + public: SSAInt(ref parent, i64 value); i64 value() const; @@ -114,7 +117,8 @@ namespace basil { class SSABool : public SSANode { bool _value; - public: + + public: SSABool(ref parent, bool value); bool value() const; @@ -125,7 +129,8 @@ namespace basil { class SSAString : public SSANode { string _value; - public: + + public: SSAString(ref parent, const string& value); const string& value() const; @@ -135,7 +140,7 @@ namespace basil { }; class SSAVoid : public SSANode { - public: + public: SSAVoid(ref parent); Location emit(Function& fn) override; @@ -144,7 +149,8 @@ namespace basil { class SSASymbol : public SSANode { u64 _value; - public: + + public: SSASymbol(ref parent, u64 value); u64 value() const; @@ -155,7 +161,8 @@ namespace basil { class SSAFunction : public SSANode { u64 _name; - public: + + public: SSAFunction(ref parent, const Type* type, u64 name); Location emit(Function& fn) override; @@ -164,7 +171,8 @@ namespace basil { class SSALoadLabel : public SSANode { u64 _name; - public: + + public: SSALoadLabel(ref parent, const Type* type, u64 name); Location emit(Function& fn) override; @@ -174,7 +182,8 @@ namespace basil { class SSAStore : public SSANode { ref _env; ref _value; - public: + + public: SSAStore(ref parent, ref env, u64 name, ref value); Location emit(Function& fn) override; @@ -183,7 +192,8 @@ namespace basil { class SSABinary : public SSANode { ref _left, _right; - public: + + public: SSABinary(ref parent, const Type* type, SSAKind kind, ref left, ref right); ref left() const; @@ -196,7 +206,8 @@ namespace basil { class SSAUnary : public SSANode { ref _child; - public: + + public: SSAUnary(ref parent, const Type* type, SSAKind kind, ref child); ref child() const; @@ -208,21 +219,22 @@ namespace basil { class SSACall : public SSANode { ref _fn; vector> _args; - - public: + + public: SSACall(ref parent, const Type* rettype, ref fn, const vector>& args); - + ref fn() const; ref& fn(); const vector>& args() const; vector>& args(); Location emit(Function& fn) override; void format(stream& io) const override; - }; + }; class SSARet : public SSANode { ref _value; - public: + + public: SSARet(ref parent, ref value); ref value() const; @@ -233,7 +245,8 @@ namespace basil { class SSAGoto : public SSANode { ref _target; - public: + + public: SSAGoto(ref parent, ref target); ref target() const; @@ -245,7 +258,8 @@ namespace basil { class SSAIf : public SSANode { ref _cond; ref _if_true, _if_false; - public: + + public: SSAIf(ref parent, ref cond, ref if_true, ref if_false); ref cond() const; @@ -261,9 +275,10 @@ namespace basil { class SSAPhi : public SSANode { ref _left, _right; ref _left_block, _right_block; - public: + + public: SSAPhi(ref parent, const Type* type, ref left, ref right, - ref left_block, ref right_block); + ref left_block, ref right_block); ref left() const; ref& left(); @@ -276,7 +291,7 @@ namespace basil { Location emit(Function& fn) override; void format(stream& io) const override; }; -} +} // namespace basil void write(stream& io, const basil::SSAIdent& id); void write(stream& io, const ref node); diff --git a/compiler/type.cpp b/compiler/type.cpp index 8c9fa1d..49ddf0a 100644 --- a/compiler/type.cpp +++ b/compiler/type.cpp @@ -2,706 +2,671 @@ namespace basil { - Type::Type(u64 hash) : _hash(hash) {} - - u64 Type::hash() const { - return _hash; - } - - bool Type::concrete() const { - return this != ANY; - } - - const Type* Type::concretify() const { - return this; - } - - bool Type::coerces_to(const Type* other) const { - return other == this - || other == ANY - || other->kind() == KIND_RUNTIME && this->coerces_to(((const RuntimeType*)other)->base()) - || this == VOID && other->kind() == KIND_LIST - || other->kind() == KIND_NAMED && ((const NamedType*)other)->base() == this - || other->kind() == KIND_SUM && ((const SumType*)other)->has(this); - } - - SingletonType::SingletonType(const string& repr) : Type(::hash(repr)), _repr(repr) {} - - TypeKind SingletonType::kind() const { - return KIND_SINGLETON; - } - - bool SingletonType::operator==(const Type& other) const { - return other.kind() == kind() && ((const SingletonType&) other)._repr == _repr; - } - - void SingletonType::format(stream& io) const { - write(io, _repr); - } - - // NumericType - - NumericType::NumericType(u32 size, bool floating): - Type(2659161638339667757ul ^ ::hash(size) ^ ::hash(floating)), - _size(size), _floating(floating) {} - - bool NumericType::floating() const { - return _floating; - } - - u32 NumericType::size() const { - return _size; - } - - TypeKind NumericType::kind() const { - return KIND_NUMERIC; - } - - bool NumericType::operator==(const Type& other) const { - return other.kind() == KIND_NUMERIC - && ((const NumericType&)other)._floating == _floating - && ((const NumericType&)other)._size == _size; - } - - bool NumericType::coerces_to(const Type* other) const { - if (Type::coerces_to(other)) return true; - if (other->kind() != KIND_NUMERIC) return false; - - bool other_floating = ((const NumericType*)other)->floating(); - u32 other_size = ((const NumericType*)other)->size(); - if (floating() == other_floating) return other_size >= size(); // both float, or both non-float - else if (!floating() && other_floating) return other_size > size(); // works for power-of-two type sizes - return false; - } - - void NumericType::format(stream& io) const { - write(io, floating() ? "f" : "i", size()); - } - - // NamedType - - NamedType::NamedType(const string& name, const Type* base): - Type(1921110990418496011ul ^ ::hash(name) ^ base->hash()), - _name(name), _base(base) {} - - const string& NamedType::name() const { - return _name; - } - - const Type* NamedType::base() const { - return _base; - } - - TypeKind NamedType::kind() const { - return KIND_NAMED; - } - - bool NamedType::operator==(const Type& other) const { - return other.kind() == KIND_NAMED - && ((const NamedType&)other)._base == _base - && ((const NamedType&)other)._name == _name; - } - - bool NamedType::coerces_to(const Type* other) const { - return Type::coerces_to(other) || other->coerces_to(_base); - } - - void NamedType::format(stream& io) const { - write(io, _name); - } - - // ListType - - ListType::ListType(const Type* element): - Type(element->hash() ^ 11340086872871314823ul), _element(element) {} - - bool ListType::concrete() const { - return _element->concrete(); - } - - const Type* ListType::concretify() const { - return find(_element->concretify()); - } - - const Type* ListType::element() const { - return _element; - } - - TypeKind ListType::kind() const { - return KIND_LIST; - } - - bool ListType::operator==(const Type& other) const { - return other.kind() == kind() && - ((const ListType&) other).element() == element(); - } - - bool ListType::coerces_to(const Type* other) const { - return Type::coerces_to(other) - || other == TYPE && _element == TYPE - || other->kind() == KIND_LIST && concrete() && !other->concrete(); - } - - void ListType::format(stream& io) const { - write(io, "[", _element, "]"); - } - - // ArrayType - - ArrayType::ArrayType(const Type* element): - Type(element->hash() ^ 11745103813974897731ul), - _element(element), _fixed(false), _size(0) {} - - ArrayType::ArrayType(const Type* element, u32 size): - Type(element->hash() ^ 5095961086520768179ul * ::hash(size)), - _element(element), _fixed(true), _size(size) {} - - const Type* ArrayType::element() const { - return _element; - } - - u32 ArrayType::count() const { - return _size; - } - - bool ArrayType::fixed() const { - return _fixed; - } - - bool ArrayType::concrete() const { - return _element->concrete(); - } - - const Type* ArrayType::concretify() const { - return _fixed ? find(_element->concretify(), _size) - : find(_element->concretify()); - } - - TypeKind ArrayType::kind() const { - return KIND_ARRAY; - } - - bool ArrayType::operator==(const Type& other) const { - return other.kind() == kind() && - ((const ArrayType&) other).element() == element() && - ((const ArrayType&) other).fixed() == fixed() && - (((const ArrayType&) other).count() == count() || !fixed()); - } - - bool ArrayType::coerces_to(const Type* other) const { - if (Type::coerces_to(other)) return true; - - if (other->kind() == KIND_ARRAY) { - if (((const ArrayType*)other)->element()->concrete() && !element()->concrete()) + Type::Type(u64 hash) : _hash(hash) {} + + u64 Type::hash() const { + return _hash; + } + + bool Type::concrete() const { + return this != ANY; + } + + const Type* Type::concretify() const { + return this; + } + + bool Type::coerces_to(const Type* other) const { + return other == this || other == ANY || + other->kind() == KIND_RUNTIME && this->coerces_to(((const RuntimeType*)other)->base()) || + this == VOID && other->kind() == KIND_LIST || + other->kind() == KIND_NAMED && ((const NamedType*)other)->base() == this || + other->kind() == KIND_SUM && ((const SumType*)other)->has(this); + } + + SingletonType::SingletonType(const string& repr) : Type(::hash(repr)), _repr(repr) {} + + TypeKind SingletonType::kind() const { + return KIND_SINGLETON; + } + + bool SingletonType::operator==(const Type& other) const { + return other.kind() == kind() && ((const SingletonType&)other)._repr == _repr; + } + + void SingletonType::format(stream& io) const { + write(io, _repr); + } + + // NumericType + + NumericType::NumericType(u32 size, bool floating) + : Type(2659161638339667757ul ^ ::hash(size) ^ ::hash(floating)), _size(size), _floating(floating) {} + + bool NumericType::floating() const { + return _floating; + } + + u32 NumericType::size() const { + return _size; + } + + TypeKind NumericType::kind() const { + return KIND_NUMERIC; + } + + bool NumericType::operator==(const Type& other) const { + return other.kind() == KIND_NUMERIC && ((const NumericType&)other)._floating == _floating && + ((const NumericType&)other)._size == _size; + } + + bool NumericType::coerces_to(const Type* other) const { + if (Type::coerces_to(other)) return true; + if (other->kind() != KIND_NUMERIC) return false; + + bool other_floating = ((const NumericType*)other)->floating(); + u32 other_size = ((const NumericType*)other)->size(); + if (floating() == other_floating) return other_size >= size(); // both float, or both non-float + else if (!floating() && other_floating) + return other_size > size(); // works for power-of-two type sizes + return false; + } + + void NumericType::format(stream& io) const { + write(io, floating() ? "f" : "i", size()); + } + + // NamedType + + NamedType::NamedType(const string& name, const Type* base) + : Type(1921110990418496011ul ^ ::hash(name) ^ base->hash()), _name(name), _base(base) {} + + const string& NamedType::name() const { + return _name; + } + + const Type* NamedType::base() const { + return _base; + } + + TypeKind NamedType::kind() const { + return KIND_NAMED; + } + + bool NamedType::operator==(const Type& other) const { + return other.kind() == KIND_NAMED && ((const NamedType&)other)._base == _base && + ((const NamedType&)other)._name == _name; + } + + bool NamedType::coerces_to(const Type* other) const { + return Type::coerces_to(other) || other->coerces_to(_base); + } + + void NamedType::format(stream& io) const { + write(io, _name); + } + + // ListType + + ListType::ListType(const Type* element) : Type(element->hash() ^ 11340086872871314823ul), _element(element) {} + + bool ListType::concrete() const { + return _element->concrete(); + } + + const Type* ListType::concretify() const { + return find(_element->concretify()); + } + + const Type* ListType::element() const { + return _element; + } + + TypeKind ListType::kind() const { + return KIND_LIST; + } + + bool ListType::operator==(const Type& other) const { + return other.kind() == kind() && ((const ListType&)other).element() == element(); + } + + bool ListType::coerces_to(const Type* other) const { + return Type::coerces_to(other) || other == TYPE && _element == TYPE || + other->kind() == KIND_LIST && concrete() && !other->concrete(); + } + + void ListType::format(stream& io) const { + write(io, "[", _element, "]"); + } + + // ArrayType + + ArrayType::ArrayType(const Type* element) + : Type(element->hash() ^ 11745103813974897731ul), _element(element), _fixed(false), _size(0) {} + + ArrayType::ArrayType(const Type* element, u32 size) + : Type(element->hash() ^ 5095961086520768179ul * ::hash(size)), _element(element), _fixed(true), _size(size) {} + + const Type* ArrayType::element() const { + return _element; + } + + u32 ArrayType::count() const { + return _size; + } + + bool ArrayType::fixed() const { + return _fixed; + } + + bool ArrayType::concrete() const { + return _element->concrete(); + } + + const Type* ArrayType::concretify() const { + return _fixed ? find(_element->concretify(), _size) : find(_element->concretify()); + } + + TypeKind ArrayType::kind() const { + return KIND_ARRAY; + } + + bool ArrayType::operator==(const Type& other) const { + return other.kind() == kind() && ((const ArrayType&)other).element() == element() && + ((const ArrayType&)other).fixed() == fixed() && + (((const ArrayType&)other).count() == count() || !fixed()); + } + + bool ArrayType::coerces_to(const Type* other) const { + if (Type::coerces_to(other)) return true; + + if (other->kind() == KIND_ARRAY) { + if (((const ArrayType*)other)->element()->concrete() && !element()->concrete()) return false; + if (((const ArrayType*)other)->fixed() && !fixed()) return false; + return ((const ArrayType*)other)->element() == element() || + !((const ArrayType*)other)->element()->concrete() && concrete(); + } + + if (fixed() && other->kind() == KIND_PRODUCT && ((const ProductType*)other)->count() == count()) { + for (u32 i = 0; i < count(); i++) + if (((const ProductType*)other)->member(i) != element()) return false; + return true; + } + return false; - if (((const ArrayType*)other)->fixed() && !fixed()) + } + + void ArrayType::format(stream& io) const { + if (_fixed) write(io, _element, "[", _size, "]"); + else + write(io, _element, "[]"); + } + + u64 set_hash(const set& members) { + u64 h = 6530804687830202173ul; + for (const Type* t : members) h ^= t->hash(); + return h; + } + + SumType::SumType(const set& members) + : Type(2853124965035107823ul ^ set_hash(members)), _members(members) {} + + bool SumType::has(const Type* member) const { + return _members.find(member) != _members.end(); + } + + TypeKind SumType::kind() const { + return KIND_SUM; + } + + bool SumType::operator==(const Type& other) const { + if (other.kind() != kind()) return false; + for (const Type* t : _members) + if (!((const SumType&)other).has(t)) return false; + return true; + } + + bool SumType::coerces_to(const Type* other) const { + if (Type::coerces_to(other)) return true; + + if (other->kind() == KIND_SUM) { + for (const Type* t : _members) + if (!((const SumType*)other)->has(t)) return false; + return true; + } + return false; - return ((const ArrayType*)other)->element() == element() - || !((const ArrayType*)other)->element()->concrete() && concrete(); - } - - if (fixed() && other->kind() == KIND_PRODUCT - && ((const ProductType*)other)->count() == count()) { - for (u32 i = 0; i < count(); i ++) - if (((const ProductType*)other)->member(i) != element()) return false; - return true; - } - - return false; - } - - void ArrayType::format(stream& io) const { - if (_fixed) write(io, _element, "[", _size, "]"); - else write(io, _element, "[]"); - } - - u64 set_hash(const set& members) { - u64 h = 6530804687830202173ul; - for (const Type* t : members) h ^= t->hash(); - return h; - } - - SumType::SumType(const set& members): - Type(2853124965035107823ul ^ set_hash(members)), _members(members) {} - - bool SumType::has(const Type* member) const { - return _members.find(member) != _members.end(); - } - - TypeKind SumType::kind() const { - return KIND_SUM; - } - - bool SumType::operator==(const Type& other) const { - if (other.kind() != kind()) return false; - for (const Type* t : _members) - if (!((const SumType&) other).has(t)) return false; - return true; - } - - bool SumType::coerces_to(const Type* other) const { - if (Type::coerces_to(other)) return true; - - if (other->kind() == KIND_SUM) { - for (const Type* t : _members) - if (!((const SumType*)other)->has(t)) return false; - return true; - } - - return false; - } - - void SumType::format(stream& io) const { - write(io, "("); - bool first = true; - for (const Type* t : _members) { - write(io, first ? "" : " | ", t); - first = false; - } - write(io, ")"); - } - - IntersectType::IntersectType(const set& members): - Type(15263450813870290249ul ^ set_hash(members)), _members(members), _has_function(false) { - for (const Type* t : _members) if (t->kind() == KIND_FUNCTION) _has_function = true; - } - - bool IntersectType::has(const Type* member) const { - return _members.find(member) != _members.end(); - } - - bool IntersectType::has_function() const { - return _has_function; - } - - TypeKind IntersectType::kind() const { - return KIND_INTERSECT; - } - - bool IntersectType::operator==(const Type& other) const { - if (other.kind() != kind()) return false; - for (const Type *t : _members) - if (!((const IntersectType&)other).has(t)) return false; - return true; - } - - bool IntersectType::coerces_to(const Type* other) const { - if (Type::coerces_to(other)) return true; - if (has(other)) return true; - if (other->kind() == KIND_INTERSECT) { - for (const Type* t : ((const IntersectType*)other)->_members) - if (!has(t)) return false; - return true; - } - return false; - } - - void IntersectType::format(stream& io) const { - write(io, "("); - bool first = true; - for (const Type* t : _members) { - write(io, first ? "" : " & ", t); - first = false; - } - write(io, ")"); - } - - u64 vector_hash(const vector& members) { - u64 h = 10472618355682807153ul; - for (const Type* t : members) h ^= t->hash(); - return h; - } - - ProductType::ProductType(const vector& members): - Type(vector_hash(members)), _members(members) {} - - u32 ProductType::count() const { - return _members.size(); - } - - const Type* ProductType::member(u32 i) const { - return _members[i]; - } - - bool ProductType::concrete() const { - for (const Type* t : _members) if (!t->concrete()) return false; - return true; - } - - const Type* ProductType::concretify() const { - vector ts = _members; - for (const Type*& t : ts) t = t->concretify(); - return find(ts); - } - - TypeKind ProductType::kind() const { - return KIND_PRODUCT; - } - - bool ProductType::operator==(const Type& other) const { - if (other.kind() != kind()) return false; - const ProductType& p = (const ProductType&)other; - if (p.count() != count()) return false; - for (u32 i = 0; i < count(); i ++) { - if (!(*p.member(i) == *member(i))) return false; - } - return true; - } - - bool ProductType::coerces_to(const Type* other) const { - if (Type::coerces_to(other)) return true; - - // if (other.kind() == KIND_ARRAY - // && ((const ArrayType&)other).element() == element() - // && !((const ArrayType&)other).fixed()) return true; - - if (other == TYPE) { - for (u32 i = 0; i < count(); i ++) - if (member(i) != TYPE) return false; - return true; - } - - if (other->kind() == KIND_ARRAY && ((const ArrayType*)other)->fixed() - && ((const ArrayType*)other)->count() == count()) { - for (u32 i = 0; i < count(); i ++) - if (member(i) != ((const ArrayType*)other)->element()) return false; - return true; - } - - return false; - } - - void ProductType::format(stream& io) const { - write(io, "("); - bool first = true; - for (const Type* t : _members) { - write(io, first ? "" : " * ", t); - first = false; - } - write(io, ")"); - } - - DictType::DictType(const Type* key, const Type* value): - Type(key->hash() * 3403329778754487247ul ^ value->hash()), _key(key), _value(value) {} - - const Type* DictType::key() const { - return _key; - } - - const Type* DictType::value() const { - return _value; - } - - bool DictType::concrete() const { - return _key->concrete() && _value->concrete(); - } - - const Type* DictType::concretify() const { - return find(_key->concretify(), _value->concretify()); - } - - bool DictType::coerces_to(const Type* other) const { - if (Type::coerces_to(other)) return true; - if (other->kind() != KIND_DICT) return false; - - bool k = !((const DictType*)other)->_key->concrete(), - v = !((const DictType*)other)->_value->concrete(); - - return k && v - || k && _value->coerces_to(((const DictType*)other)->_value) - || v && _key->coerces_to(((const DictType*)other)->_key); - } - - TypeKind DictType::kind() const { - return KIND_DICT; - } - - bool DictType::operator==(const Type& other) const { - return other.kind() == KIND_DICT - && ((const DictType&)other)._key == _key - && ((const DictType&)other)._value == _value; - } - - void DictType::format(stream& io) const { - write(io, _value, "[", _key, "]"); - } - - FunctionType::FunctionType(const Type* arg, const Type* ret): - Type(arg->hash() ^ ret->hash() ^ 17623206604232272301ul), - _arg(arg), _ret(ret) {} - - const Type* FunctionType::arg() const { - return _arg; - } - - const Type* FunctionType::ret() const { - return _ret; - } - - u32 FunctionType::arity() const { - return _arg->kind() == KIND_PRODUCT ? - ((const ProductType*)_arg)->count() : 1; - } - - bool FunctionType::concrete() const { - return _arg->concrete() && _ret->concrete(); - } - - const Type* FunctionType::concretify() const { - return find(_arg->concretify(), _ret->concretify()); - } - - TypeKind FunctionType::kind() const { - return KIND_FUNCTION; - } - - bool FunctionType::operator==(const Type& other) const { - if (other.kind() != kind()) return false; - const FunctionType& f = (const FunctionType&)other; - return *f._arg == *_arg && *f._ret == *_ret; - } - - void FunctionType::format(stream& io) const { - write(io, "(", _arg, " -> ", _ret, ")"); - } - - AliasType::AliasType(): - Type(9323462044786133851ul) {} - - TypeKind AliasType::kind() const { - return KIND_ALIAS; - } - - bool AliasType::operator==(const Type& other) const { - return other.kind() == kind(); - } - - void AliasType::format(stream& io) const { - write(io, "alias"); - } - - MacroType::MacroType(u32 arity): - Type(18254210403858406693ul ^ ::hash(arity)), _arity(arity) {} - - u32 MacroType::arity() const { - return _arity; - } - - TypeKind MacroType::kind() const { - return KIND_MACRO; - } - - bool MacroType::operator==(const Type& other) const { - return other.kind() == kind() && - ((const MacroType&)other).arity() == arity(); - } - - void MacroType::format(stream& io) const { - write(io, "macro(", _arity, ")"); - } - - RuntimeType::RuntimeType(const Type* base): - Type(base->hash() ^ 5857490642180150551ul), _base(base) {} - - const Type* RuntimeType::base() const { - return _base; - } - - TypeKind RuntimeType::kind() const { - return KIND_RUNTIME; - } - - bool RuntimeType::operator==(const Type& other) const { - return other.kind() == kind() && - *((const RuntimeType&)other).base() == *base(); - } - - bool RuntimeType::coerces_to(const Type* other) const { - return Type::coerces_to(other) - || other->kind() == KIND_RUNTIME && base()->coerces_to(((const RuntimeType*)other)->base()); - } - - void RuntimeType::format(stream& io) const { - write(io, "runtime<", _base, ">"); - } - - static vector type_variables; - static vector typevar_names; - - static string next_typevar() { - buffer b; - write(b, "'T", type_variables.size()); - string s; - read(b, s); - return s; - } - - static u32 create_typevar() { - type_variables.push(ANY); - typevar_names.push(next_typevar()); - return type_variables.size() - 1; - } - - TypeVariable::TypeVariable(u32 id): - Type(::hash(id) ^ 3860592187614349697ul), _id(id) {} - - TypeVariable::TypeVariable(): - TypeVariable(create_typevar()) {} - - const Type* TypeVariable::actual() const { - return type_variables[_id]; - } - - void TypeVariable::bind(const Type* concrete) const { - type_variables[_id] = concrete; - } - - bool TypeVariable::concrete() const { - return type_variables[_id]->concrete(); - } - - const Type* TypeVariable::concretify() const { - const Type* t = actual(); - if (t == ANY) return this; - return t->concretify(); - } - - TypeKind TypeVariable::kind() const { - return KIND_TYPEVAR; - } - - bool TypeVariable::operator==(const Type& other) const { - return other.kind() == kind() && _id == ((const TypeVariable&)other)._id; - } - - bool TypeVariable::coerces_to(const Type* other) const { - return Type::coerces_to(other) || !concrete() || actual()->coerces_to(other); - } - - void TypeVariable::format(stream& io) const { - write(io, typevar_names[_id]); - if (type_variables[_id]->concrete()) - write(io, "(", type_variables[_id], ")"); - } - - struct TypeBox { - const Type* t; - - bool operator==(const TypeBox& other) const { - return *t == *other.t; - } - }; - - static map TYPEMAP; - - const Type* find_existing_type(const Type* t) { - auto it = TYPEMAP.find({ t }); - if (it == TYPEMAP.end()) return nullptr; - return it->second; - } - - const Type* create_type(const Type* t) { - TYPEMAP.put({ t }, t); - return t; - } - - const Type *INT = find(64, false), - *FLOAT = find(64, true), - *SYMBOL = find("symbol"), - *VOID = find("void"), - *ERROR = find("error"), - *TYPE = find("type"), - *ALIAS = find(), - *BOOL = find("bool"), - *ANY = find("any"), - *STRING = find("string"), - *MODULE = find("module"); - - const Type* unify(const Type* a, const Type* b, bool coercing, bool converting) { - if (!a || !b) return nullptr; - - if (a == ANY) return b; - else if (b == ANY) return a; - - if (a->kind() == KIND_TYPEVAR) { - if (!((const TypeVariable*)a)->actual()->concrete()) { - if (b->concrete() || b->kind() != KIND_TYPEVAR) - return ((const TypeVariable*)a)->bind(b), b; - else if (b > a) - return ((const TypeVariable*)a)->bind(b), b; - else return a; - } - else a = ((const TypeVariable*)a)->actual(); - } - - if (b->kind() == KIND_TYPEVAR) { - if (!((const TypeVariable*)b)->actual()->concrete()) { - if (a->concrete() || a->kind() != KIND_TYPEVAR) - return ((const TypeVariable*)b)->bind(a), a; - else if (a > b) - return ((const TypeVariable*)b)->bind(a), a; - else return b; - } - b = ((const TypeVariable*)b)->actual(); - } - - if (a->kind() == KIND_LIST && b->kind() == KIND_LIST) { - const Type* elt = unify(((const ListType*)a)->element(), - ((const ListType*)b)->element()); - if (!elt) return nullptr; - return find(elt); - } - - if (a->kind() == KIND_PRODUCT && b->kind() == KIND_PRODUCT) { - vector members; - if (((const ProductType*)a)->count() != ((const ProductType*)b)->count()) - return nullptr; - for (u32 i = 0; i < ((const ProductType*)a)->count(); i ++) { - const Type* member = unify(((const ProductType*)a)->member(i), - ((const ProductType*)b)->member(i)); - if (!member) return nullptr; - members.push(member); - } - return find(members); - } - - if (a->kind() == KIND_FUNCTION && b->kind() == KIND_FUNCTION) { - const Type* arg = unify(((const FunctionType*)a)->arg(), - ((const FunctionType*)b)->arg()); - const Type* ret = unify(((const FunctionType*)a)->ret(), - ((const FunctionType*)b)->ret()); - if (!arg || !ret) return nullptr; - return find(arg, ret); - } - - if (a == VOID && b->kind() == KIND_LIST) return b; - if (b == VOID && a->kind() == KIND_LIST) return a; - - if (coercing || converting) { - if (a->coerces_to(b)) return b; - if (b->coerces_to(a)) return a; - } - - if (a != b) return nullptr; - return a; - } -} + } + + void SumType::format(stream& io) const { + write(io, "("); + bool first = true; + for (const Type* t : _members) { + write(io, first ? "" : " | ", t); + first = false; + } + write(io, ")"); + } + + IntersectType::IntersectType(const set& members) + : Type(15263450813870290249ul ^ set_hash(members)), _members(members), _has_function(false) { + for (const Type* t : _members) + if (t->kind() == KIND_FUNCTION) _has_function = true; + } + + bool IntersectType::has(const Type* member) const { + return _members.find(member) != _members.end(); + } + + bool IntersectType::has_function() const { + return _has_function; + } + + TypeKind IntersectType::kind() const { + return KIND_INTERSECT; + } + + bool IntersectType::operator==(const Type& other) const { + if (other.kind() != kind()) return false; + for (const Type* t : _members) + if (!((const IntersectType&)other).has(t)) return false; + return true; + } + + bool IntersectType::coerces_to(const Type* other) const { + if (Type::coerces_to(other)) return true; + if (has(other)) return true; + if (other->kind() == KIND_INTERSECT) { + for (const Type* t : ((const IntersectType*)other)->_members) + if (!has(t)) return false; + return true; + } + return false; + } + + void IntersectType::format(stream& io) const { + write(io, "("); + bool first = true; + for (const Type* t : _members) { + write(io, first ? "" : " & ", t); + first = false; + } + write(io, ")"); + } + + u64 vector_hash(const vector& members) { + u64 h = 10472618355682807153ul; + for (const Type* t : members) h ^= t->hash(); + return h; + } + + ProductType::ProductType(const vector& members) : Type(vector_hash(members)), _members(members) {} + + u32 ProductType::count() const { + return _members.size(); + } + + const Type* ProductType::member(u32 i) const { + return _members[i]; + } + + bool ProductType::concrete() const { + for (const Type* t : _members) + if (!t->concrete()) return false; + return true; + } + + const Type* ProductType::concretify() const { + vector ts = _members; + for (const Type*& t : ts) t = t->concretify(); + return find(ts); + } + + TypeKind ProductType::kind() const { + return KIND_PRODUCT; + } + + bool ProductType::operator==(const Type& other) const { + if (other.kind() != kind()) return false; + const ProductType& p = (const ProductType&)other; + if (p.count() != count()) return false; + for (u32 i = 0; i < count(); i++) { + if (!(*p.member(i) == *member(i))) return false; + } + return true; + } + + bool ProductType::coerces_to(const Type* other) const { + if (Type::coerces_to(other)) return true; + + // if (other.kind() == KIND_ARRAY + // && ((const ArrayType&)other).element() == element() + // && !((const ArrayType&)other).fixed()) return true; + + if (other == TYPE) { + for (u32 i = 0; i < count(); i++) + if (member(i) != TYPE) return false; + return true; + } + + if (other->kind() == KIND_ARRAY && ((const ArrayType*)other)->fixed() && + ((const ArrayType*)other)->count() == count()) { + for (u32 i = 0; i < count(); i++) + if (member(i) != ((const ArrayType*)other)->element()) return false; + return true; + } + + return false; + } + + void ProductType::format(stream& io) const { + write(io, "("); + bool first = true; + for (const Type* t : _members) { + write(io, first ? "" : " * ", t); + first = false; + } + write(io, ")"); + } + + DictType::DictType(const Type* key, const Type* value) + : Type(key->hash() * 3403329778754487247ul ^ value->hash()), _key(key), _value(value) {} + + const Type* DictType::key() const { + return _key; + } + + const Type* DictType::value() const { + return _value; + } + + bool DictType::concrete() const { + return _key->concrete() && _value->concrete(); + } + + const Type* DictType::concretify() const { + return find(_key->concretify(), _value->concretify()); + } + + bool DictType::coerces_to(const Type* other) const { + if (Type::coerces_to(other)) return true; + if (other->kind() != KIND_DICT) return false; + + bool k = !((const DictType*)other)->_key->concrete(), v = !((const DictType*)other)->_value->concrete(); + + return k && v || k && _value->coerces_to(((const DictType*)other)->_value) || + v && _key->coerces_to(((const DictType*)other)->_key); + } + + TypeKind DictType::kind() const { + return KIND_DICT; + } + + bool DictType::operator==(const Type& other) const { + return other.kind() == KIND_DICT && ((const DictType&)other)._key == _key && + ((const DictType&)other)._value == _value; + } + + void DictType::format(stream& io) const { + write(io, _value, "[", _key, "]"); + } + + FunctionType::FunctionType(const Type* arg, const Type* ret) + : Type(arg->hash() ^ ret->hash() ^ 17623206604232272301ul), _arg(arg), _ret(ret) {} + + const Type* FunctionType::arg() const { + return _arg; + } + + const Type* FunctionType::ret() const { + return _ret; + } + + u32 FunctionType::arity() const { + return _arg->kind() == KIND_PRODUCT ? ((const ProductType*)_arg)->count() : 1; + } + + bool FunctionType::concrete() const { + return _arg->concrete() && _ret->concrete(); + } + + const Type* FunctionType::concretify() const { + return find(_arg->concretify(), _ret->concretify()); + } + + TypeKind FunctionType::kind() const { + return KIND_FUNCTION; + } + + bool FunctionType::operator==(const Type& other) const { + if (other.kind() != kind()) return false; + const FunctionType& f = (const FunctionType&)other; + return *f._arg == *_arg && *f._ret == *_ret; + } + + void FunctionType::format(stream& io) const { + write(io, "(", _arg, " -> ", _ret, ")"); + } + + AliasType::AliasType() : Type(9323462044786133851ul) {} + + TypeKind AliasType::kind() const { + return KIND_ALIAS; + } + + bool AliasType::operator==(const Type& other) const { + return other.kind() == kind(); + } + + void AliasType::format(stream& io) const { + write(io, "alias"); + } + + MacroType::MacroType(u32 arity) : Type(18254210403858406693ul ^ ::hash(arity)), _arity(arity) {} + + u32 MacroType::arity() const { + return _arity; + } + + TypeKind MacroType::kind() const { + return KIND_MACRO; + } + + bool MacroType::operator==(const Type& other) const { + return other.kind() == kind() && ((const MacroType&)other).arity() == arity(); + } + + void MacroType::format(stream& io) const { + write(io, "macro(", _arity, ")"); + } + + RuntimeType::RuntimeType(const Type* base) : Type(base->hash() ^ 5857490642180150551ul), _base(base) {} + + const Type* RuntimeType::base() const { + return _base; + } + + TypeKind RuntimeType::kind() const { + return KIND_RUNTIME; + } + + bool RuntimeType::operator==(const Type& other) const { + return other.kind() == kind() && *((const RuntimeType&)other).base() == *base(); + } + + bool RuntimeType::coerces_to(const Type* other) const { + return Type::coerces_to(other) || + other->kind() == KIND_RUNTIME && base()->coerces_to(((const RuntimeType*)other)->base()); + } + + void RuntimeType::format(stream& io) const { + write(io, "runtime<", _base, ">"); + } + + static vector type_variables; + static vector typevar_names; + + static string next_typevar() { + buffer b; + write(b, "'T", type_variables.size()); + string s; + read(b, s); + return s; + } + + static u32 create_typevar() { + type_variables.push(ANY); + typevar_names.push(next_typevar()); + return type_variables.size() - 1; + } + + TypeVariable::TypeVariable(u32 id) : Type(::hash(id) ^ 3860592187614349697ul), _id(id) {} + + TypeVariable::TypeVariable() : TypeVariable(create_typevar()) {} + + const Type* TypeVariable::actual() const { + return type_variables[_id]; + } + + void TypeVariable::bind(const Type* concrete) const { + type_variables[_id] = concrete; + } + + bool TypeVariable::concrete() const { + return type_variables[_id]->concrete(); + } + + const Type* TypeVariable::concretify() const { + const Type* t = actual(); + if (t == ANY) return this; + return t->concretify(); + } + + TypeKind TypeVariable::kind() const { + return KIND_TYPEVAR; + } + + bool TypeVariable::operator==(const Type& other) const { + return other.kind() == kind() && _id == ((const TypeVariable&)other)._id; + } + + bool TypeVariable::coerces_to(const Type* other) const { + return Type::coerces_to(other) || !concrete() || actual()->coerces_to(other); + } + + void TypeVariable::format(stream& io) const { + write(io, typevar_names[_id]); + if (type_variables[_id]->concrete()) write(io, "(", type_variables[_id], ")"); + } + + struct TypeBox { + const Type* t; + + bool operator==(const TypeBox& other) const { + return *t == *other.t; + } + }; + + static map TYPEMAP; + + const Type* find_existing_type(const Type* t) { + auto it = TYPEMAP.find({t}); + if (it == TYPEMAP.end()) return nullptr; + return it->second; + } + + const Type* create_type(const Type* t) { + TYPEMAP.put({t}, t); + return t; + } + + const Type *INT = find(64, false), *FLOAT = find(64, true), + *SYMBOL = find("symbol"), *VOID = find("void"), + *ERROR = find("error"), *TYPE = find("type"), *ALIAS = find(), + *BOOL = find("bool"), *ANY = find("any"), + *STRING = find("string"), *MODULE = find("module"); + + const Type* unify(const Type* a, const Type* b, bool coercing, bool converting) { + if (!a || !b) return nullptr; + + if (a == ANY) return b; + else if (b == ANY) + return a; + + if (a->kind() == KIND_TYPEVAR) { + if (!((const TypeVariable*)a)->actual()->concrete()) { + if (b->concrete() || b->kind() != KIND_TYPEVAR) return ((const TypeVariable*)a)->bind(b), b; + else if (b > a) + return ((const TypeVariable*)a)->bind(b), b; + else + return a; + } else + a = ((const TypeVariable*)a)->actual(); + } + + if (b->kind() == KIND_TYPEVAR) { + if (!((const TypeVariable*)b)->actual()->concrete()) { + if (a->concrete() || a->kind() != KIND_TYPEVAR) return ((const TypeVariable*)b)->bind(a), a; + else if (a > b) + return ((const TypeVariable*)b)->bind(a), a; + else + return b; + } + b = ((const TypeVariable*)b)->actual(); + } + + if (a->kind() == KIND_LIST && b->kind() == KIND_LIST) { + const Type* elt = unify(((const ListType*)a)->element(), ((const ListType*)b)->element()); + if (!elt) return nullptr; + return find(elt); + } + + if (a->kind() == KIND_PRODUCT && b->kind() == KIND_PRODUCT) { + vector members; + if (((const ProductType*)a)->count() != ((const ProductType*)b)->count()) return nullptr; + for (u32 i = 0; i < ((const ProductType*)a)->count(); i++) { + const Type* member = unify(((const ProductType*)a)->member(i), ((const ProductType*)b)->member(i)); + if (!member) return nullptr; + members.push(member); + } + return find(members); + } + + if (a->kind() == KIND_FUNCTION && b->kind() == KIND_FUNCTION) { + const Type* arg = unify(((const FunctionType*)a)->arg(), ((const FunctionType*)b)->arg()); + const Type* ret = unify(((const FunctionType*)a)->ret(), ((const FunctionType*)b)->ret()); + if (!arg || !ret) return nullptr; + return find(arg, ret); + } + + if (a == VOID && b->kind() == KIND_LIST) return b; + if (b == VOID && a->kind() == KIND_LIST) return a; + + if (coercing || converting) { + if (a->coerces_to(b)) return b; + if (b->coerces_to(a)) return a; + } + + if (a != b) return nullptr; + return a; + } +} // namespace basil -template<> +template <> u64 hash(const basil::Type& t) { - return t.hash(); + return t.hash(); } -template<> +template <> u64 hash(const basil::Type* const& t) { - return t->hash(); + return t->hash(); } -template<> +template <> u64 hash(const basil::TypeBox& t) { - return t.t->hash(); + return t.t->hash(); } void write(stream& io, const basil::Type* t) { - t->format(io); + t->format(io); } \ No newline at end of file diff --git a/compiler/type.h b/compiler/type.h index 663b6dd..1cbfe66 100644 --- a/compiler/type.h +++ b/compiler/type.h @@ -2,265 +2,279 @@ #define BASIL_TYPE_H #include "util/defs.h" -#include "util/str.h" #include "util/hash.h" #include "util/io.h" +#include "util/str.h" #include "util/vec.h" namespace basil { - const u8 GC_KIND_FLAG = 128; - - enum TypeKind : u8 { - KIND_SINGLETON = 0, - KIND_TYPEVAR = 1, - KIND_NUMERIC = 2, - KIND_LIST = GC_KIND_FLAG | 0, - KIND_SUM = GC_KIND_FLAG | 1, - KIND_INTERSECT = GC_KIND_FLAG | 2, - KIND_PRODUCT = GC_KIND_FLAG | 3, - KIND_ARRAY = GC_KIND_FLAG | 4, - KIND_FUNCTION = GC_KIND_FLAG | 5, - KIND_ALIAS = GC_KIND_FLAG | 6, - KIND_MACRO = GC_KIND_FLAG | 7, - KIND_RUNTIME = GC_KIND_FLAG | 8, - KIND_NAMED = GC_KIND_FLAG | 9, - KIND_DICT = GC_KIND_FLAG | 10 - }; - - class Type { - u64 _hash; - protected: - Type(u64 hash); - - public: - virtual TypeKind kind() const = 0; - u64 hash() const; - virtual bool concrete() const; - virtual const Type* concretify() const; - virtual bool operator==(const Type& other) const = 0; - virtual bool coerces_to(const Type* other) const; - virtual void format(stream& io) const = 0; - }; - - class SingletonType : public Type { - string _repr; - public: - SingletonType(const string& repr); - - TypeKind kind() const override; - bool operator==(const Type& other) const override; - void format(stream& io) const override; - }; - - class NumericType : public Type { - u32 _size; - bool _floating; - public: - NumericType(u32 size, bool floating); - - bool floating() const; - u32 size() const; - TypeKind kind() const override; - bool operator==(const Type& other) const override; - bool coerces_to(const Type* other) const override; - void format(stream& io) const override; - }; - - class NamedType : public Type { - string _name; - const Type* _base; - public: - NamedType(const string& name, const Type* base); - - const string& name() const; - const Type* base() const; - TypeKind kind() const override; - bool operator==(const Type& other) const override; - bool coerces_to(const Type* other) const override; - void format(stream& io) const override; - }; - - class ListType : public Type { - const Type* _element; - public: - ListType(const Type* element); - - const Type* element() const; - bool concrete() const override; - const Type* concretify() const override; - TypeKind kind() const override; - bool operator==(const Type& other) const override; - bool coerces_to(const Type* other) const override; - void format(stream& io) const override; - }; - - class ArrayType : public Type { - const Type* _element; - u32 _size; - bool _fixed; - public: - ArrayType(const Type* element); - ArrayType(const Type* element, u32 size); - - const Type* element() const; - u32 count() const; - bool fixed() const; - bool concrete() const override; - const Type* concretify() const override; - TypeKind kind() const override; - bool operator==(const Type& other) const override; - bool coerces_to(const Type* other) const override; - void format(stream& io) const override; - }; - - class SumType : public Type { - set _members; - public: - SumType(const set& members); - - bool has(const Type* member) const; - TypeKind kind() const override; - bool operator==(const Type& other) const override; - bool coerces_to(const Type* other) const override; - void format(stream& io) const override; - }; - - class IntersectType : public Type { - set _members; - bool _has_function; - public: - template - IntersectType(const Args&... args): IntersectType(set_of(args...)) {} - IntersectType(const set& members); - - bool has(const Type* member) const; - bool has_function() const; - TypeKind kind() const override; - bool operator==(const Type& other) const override; - bool coerces_to(const Type* other) const override; - void format(stream& io) const override; - }; - - class ProductType : public Type { - vector _members; - public: - template - ProductType(const Args&... args): ProductType(vector_of(args...)) {} - ProductType(const vector& members); - - u32 count() const; - const Type* member(u32 i) const; - bool concrete() const override; - const Type* concretify() const override; - TypeKind kind() const override; - bool operator==(const Type& other) const override; - bool coerces_to(const Type* other) const override; - void format(stream& io) const override; - }; - - class DictType : public Type { - const Type* _key, *_value; - public: - DictType(const Type* key, const Type* value); - - const Type* key() const; - const Type* value() const; - bool concrete() const override; - const Type* concretify() const override; - bool coerces_to(const Type* other) const override; - TypeKind kind() const override; - bool operator==(const Type& other) const override; - void format(stream& io) const override; - }; - - class FunctionType : public Type { - const Type *_arg, *_ret; - public: - FunctionType(const Type* arg, const Type* ret); - - const Type* arg() const; - const Type* ret() const; - u32 arity() const; - bool concrete() const override; - const Type* concretify() const override; - TypeKind kind() const override; - bool operator==(const Type& other) const override; - void format(stream& io) const override; - }; - - class AliasType : public Type { - public: - AliasType(); - - TypeKind kind() const override; - bool operator==(const Type& other) const override; - void format(stream& io) const override; - }; - - class MacroType : public Type { - u32 _arity; - public: - MacroType(u32 arity); - - u32 arity() const; - - TypeKind kind() const override; - bool operator==(const Type& other) const override; - void format(stream& io) const override; - }; - - class RuntimeType : public Type { - const Type* _base; - public: - RuntimeType(const Type* base); - - const Type* base() const; - TypeKind kind() const override; - bool operator==(const Type& other) const override; - bool coerces_to(const Type* other) const override; - void format(stream& io) const override; - }; - - class TypeVariable : public Type { - u32 _id; - friend void bind(const Type* a, const Type* b); - protected: - TypeVariable(u32 id); - public: - TypeVariable(); - - const Type* actual() const; - void bind(const Type* concrete) const; - bool concrete() const override; - const Type* concretify() const override; - TypeKind kind() const override; - bool operator==(const Type& other) const override; - bool coerces_to(const Type* other) const override; - void format(stream& io) const override; - }; - - const Type* find_existing_type(const Type* t); - const Type* create_type(const Type* t); - - template - const Type* find(Args... args) { - T t(args...); - const Type* e = find_existing_type(&t); - - if (e) return e; - return create_type(new T(t)); - } - - extern const Type *INT, *FLOAT, *SYMBOL, *VOID, *ERROR, *TYPE, - *ALIAS, *BOOL, *ANY, *STRING, *MODULE; - - const Type* unify(const Type* a, const Type* b, bool coercing = false, bool converting = false); -} - -template<> + const u8 GC_KIND_FLAG = 128; + + enum TypeKind : u8 { + KIND_SINGLETON = 0, + KIND_TYPEVAR = 1, + KIND_NUMERIC = 2, + KIND_LIST = GC_KIND_FLAG | 0, + KIND_SUM = GC_KIND_FLAG | 1, + KIND_INTERSECT = GC_KIND_FLAG | 2, + KIND_PRODUCT = GC_KIND_FLAG | 3, + KIND_ARRAY = GC_KIND_FLAG | 4, + KIND_FUNCTION = GC_KIND_FLAG | 5, + KIND_ALIAS = GC_KIND_FLAG | 6, + KIND_MACRO = GC_KIND_FLAG | 7, + KIND_RUNTIME = GC_KIND_FLAG | 8, + KIND_NAMED = GC_KIND_FLAG | 9, + KIND_DICT = GC_KIND_FLAG | 10 + }; + + class Type { + u64 _hash; + + protected: + Type(u64 hash); + + public: + virtual TypeKind kind() const = 0; + u64 hash() const; + virtual bool concrete() const; + virtual const Type* concretify() const; + virtual bool operator==(const Type& other) const = 0; + virtual bool coerces_to(const Type* other) const; + virtual void format(stream& io) const = 0; + }; + + class SingletonType : public Type { + string _repr; + + public: + SingletonType(const string& repr); + + TypeKind kind() const override; + bool operator==(const Type& other) const override; + void format(stream& io) const override; + }; + + class NumericType : public Type { + u32 _size; + bool _floating; + + public: + NumericType(u32 size, bool floating); + + bool floating() const; + u32 size() const; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + bool coerces_to(const Type* other) const override; + void format(stream& io) const override; + }; + + class NamedType : public Type { + string _name; + const Type* _base; + + public: + NamedType(const string& name, const Type* base); + + const string& name() const; + const Type* base() const; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + bool coerces_to(const Type* other) const override; + void format(stream& io) const override; + }; + + class ListType : public Type { + const Type* _element; + + public: + ListType(const Type* element); + + const Type* element() const; + bool concrete() const override; + const Type* concretify() const override; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + bool coerces_to(const Type* other) const override; + void format(stream& io) const override; + }; + + class ArrayType : public Type { + const Type* _element; + u32 _size; + bool _fixed; + + public: + ArrayType(const Type* element); + ArrayType(const Type* element, u32 size); + + const Type* element() const; + u32 count() const; + bool fixed() const; + bool concrete() const override; + const Type* concretify() const override; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + bool coerces_to(const Type* other) const override; + void format(stream& io) const override; + }; + + class SumType : public Type { + set _members; + + public: + SumType(const set& members); + + bool has(const Type* member) const; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + bool coerces_to(const Type* other) const override; + void format(stream& io) const override; + }; + + class IntersectType : public Type { + set _members; + bool _has_function; + + public: + template + IntersectType(const Args&... args) : IntersectType(set_of(args...)) {} + IntersectType(const set& members); + + bool has(const Type* member) const; + bool has_function() const; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + bool coerces_to(const Type* other) const override; + void format(stream& io) const override; + }; + + class ProductType : public Type { + vector _members; + + public: + template + ProductType(const Args&... args) : ProductType(vector_of(args...)) {} + ProductType(const vector& members); + + u32 count() const; + const Type* member(u32 i) const; + bool concrete() const override; + const Type* concretify() const override; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + bool coerces_to(const Type* other) const override; + void format(stream& io) const override; + }; + + class DictType : public Type { + const Type *_key, *_value; + + public: + DictType(const Type* key, const Type* value); + + const Type* key() const; + const Type* value() const; + bool concrete() const override; + const Type* concretify() const override; + bool coerces_to(const Type* other) const override; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + void format(stream& io) const override; + }; + + class FunctionType : public Type { + const Type *_arg, *_ret; + + public: + FunctionType(const Type* arg, const Type* ret); + + const Type* arg() const; + const Type* ret() const; + u32 arity() const; + bool concrete() const override; + const Type* concretify() const override; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + void format(stream& io) const override; + }; + + class AliasType : public Type { + public: + AliasType(); + + TypeKind kind() const override; + bool operator==(const Type& other) const override; + void format(stream& io) const override; + }; + + class MacroType : public Type { + u32 _arity; + + public: + MacroType(u32 arity); + + u32 arity() const; + + TypeKind kind() const override; + bool operator==(const Type& other) const override; + void format(stream& io) const override; + }; + + class RuntimeType : public Type { + const Type* _base; + + public: + RuntimeType(const Type* base); + + const Type* base() const; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + bool coerces_to(const Type* other) const override; + void format(stream& io) const override; + }; + + class TypeVariable : public Type { + u32 _id; + friend void bind(const Type* a, const Type* b); + + protected: + TypeVariable(u32 id); + + public: + TypeVariable(); + + const Type* actual() const; + void bind(const Type* concrete) const; + bool concrete() const override; + const Type* concretify() const override; + TypeKind kind() const override; + bool operator==(const Type& other) const override; + bool coerces_to(const Type* other) const override; + void format(stream& io) const override; + }; + + const Type* find_existing_type(const Type* t); + const Type* create_type(const Type* t); + + template + const Type* find(Args... args) { + T t(args...); + const Type* e = find_existing_type(&t); + + if (e) return e; + return create_type(new T(t)); + } + + extern const Type *INT, *FLOAT, *SYMBOL, *VOID, *ERROR, *TYPE, *ALIAS, *BOOL, *ANY, *STRING, *MODULE; + + const Type* unify(const Type* a, const Type* b, bool coercing = false, bool converting = false); +} // namespace basil + +template <> u64 hash(const basil::Type& t); -template<> +template <> u64 hash(const basil::Type* const& t); void write(stream& io, const basil::Type* t); diff --git a/compiler/values.cpp b/compiler/values.cpp index a2b4df8..174f024 100644 --- a/compiler/values.cpp +++ b/compiler/values.cpp @@ -107,8 +107,7 @@ namespace basil { _data.rc = m; } - Value::Value(ASTNode* n) : - _type(n->type()->kind() == KIND_RUNTIME ? n->type() : find(n->type())) { + Value::Value(ASTNode* n) : _type(n->type()->kind() == KIND_RUNTIME ? n->type() : find(n->type())) { _data.rc = n; } @@ -316,8 +315,8 @@ namespace basil { ASTNode*& Value::get_runtime() { return (ASTNode*&)_data.rc; - } - + } + bool Value::is_named() const { return _type->kind() == KIND_NAMED; } @@ -406,7 +405,7 @@ namespace basil { write(io, first ? "" : " ", pair.first); if (dt->value() != VOID) write(io, ": ", pair.second); first = false; - } + } write(io, "}"); } else if (is_function()) write(io, "<#", get_function().name() < 0 ? "procedure" : symbol_for(get_function().name()), ">"); @@ -459,7 +458,7 @@ namespace basil { for (const Value& v : get_array()) h ^= v.hash(); return h; } else if (is_dict()) { - u64 h = 13974436514101026401ul; + u64 h = 13974436514101026401ul; for (const auto& p : get_dict()) h ^= 14259444292234844953ul * p.first.hash() ^ p.second.hash(); return h; } else if (is_function()) { @@ -562,8 +561,7 @@ namespace basil { } else if (is_runtime()) { return _data.rc == other._data.rc; } else if (is_module()) { - if (get_module().entries().size() != other.get_module().entries().size()) - return false; + if (get_module().entries().size() != other.get_module().entries().size()) return false; for (auto& p : get_module().entries()) { auto it = other.get_module().entries().find(p.first); if (it == other.get_module().entries().end()) return false; @@ -580,10 +578,9 @@ namespace basil { const ListValue* i = &get_list(); vector vals; while (i) vals.push(&i->head()), i = i->tail().is_void() ? nullptr : &i->tail().get_list(); - for (i64 i = i64(vals.size()) - 1; i >= 0; i --) l = new ListValue(vals[i]->clone(), l ? l : empty()); + for (i64 i = i64(vals.size()) - 1; i >= 0; i--) l = new ListValue(vals[i]->clone(), l ? l : empty()); return Value(l); - } - else if (is_string()) + } else if (is_string()) return Value(get_string(), STRING); else if (is_named()) return Value(new NamedValue(get_named().get().clone()), type()); @@ -656,8 +653,7 @@ namespace basil { return _value; } - NamedValue::NamedValue(const Value& inner): - _inner(inner) {} + NamedValue::NamedValue(const Value& inner) : _inner(inner) {} Value& NamedValue::get() { return _inner; @@ -765,8 +761,7 @@ namespace basil { ArrayValue::ArrayValue(const vector& values) : ProductValue(values) {} - DictValue::DictValue(const map& entries): - _entries(entries) {} + DictValue::DictValue(const map& entries) : _entries(entries) {} u32 DictValue::size() const { return _entries.size(); @@ -775,13 +770,15 @@ namespace basil { Value* DictValue::operator[](const Value& key) { auto it = _entries.find(key); if (it == _entries.end()) return nullptr; - else return &it->second; + else + return &it->second; } const Value* DictValue::operator[](const Value& key) const { auto it = _entries.find(key); if (it == _entries.end()) return nullptr; - else return &it->second; + else + return &it->second; } const map::const_iterator DictValue::begin() const { @@ -798,7 +795,7 @@ namespace basil { map::iterator DictValue::end() { return _entries.end(); - } + } const map& DictValue::entries() const { return _entries; @@ -977,8 +974,7 @@ namespace basil { return _code; } - ModuleValue::ModuleValue(const map& entries): - _entries(entries) {} + ModuleValue::ModuleValue(const map& entries) : _entries(entries) {} const map& ModuleValue::entries() const { return _entries; @@ -1287,8 +1283,8 @@ namespace basil { return Value(new ArrayValue(elements)); } - Value dict_of(const map &elements) { - for (const auto &p : elements) { + Value dict_of(const map& elements) { + for (const auto& p : elements) { if (p.first.is_runtime() || p.second.is_runtime()) { err(p.first.loc(), "Cannot compile arrays yet."); return error(); @@ -1381,7 +1377,7 @@ namespace basil { Value cast(const Value& val, const Type* type) { if (val.type() == type || type == ANY) return val; - if (val.type()->kind() == KIND_TYPEVAR) { + if (val.type()->kind() == KIND_TYPEVAR) { unify(val.type(), type); return val; } @@ -1391,14 +1387,11 @@ namespace basil { return ((const RuntimeType*)type)->base() == ANY ? val : lower(val); } - if (val.type()->kind() == type->kind() && !type->concrete()) { - return val; - } + if (val.type()->kind() == type->kind() && !type->concrete()) { return val; } if (type->kind() == KIND_NAMED) { return Value(new NamedValue(val), type); - } - else if (val.type()->kind() == KIND_NAMED && type == ((const NamedType*)val.type())->base()) { + } else if (val.type()->kind() == KIND_NAMED && type == ((const NamedType*)val.type())->base()) { return val.get_named().get(); } @@ -1410,8 +1403,8 @@ namespace basil { } } - if (val.type()->kind() == KIND_ARRAY && type->kind() == KIND_ARRAY - && ((const ArrayType*)val.type())->fixed() && !((const ArrayType*)type)->fixed()) { + if (val.type()->kind() == KIND_ARRAY && type->kind() == KIND_ARRAY && ((const ArrayType*)val.type())->fixed() && + !((const ArrayType*)type)->fixed()) { return Value(new ArrayValue(val.get_array()), type); } @@ -1530,14 +1523,13 @@ namespace basil { err(args.loc(), "Expected product value for arguments."); return error(); } - for (u32 i = 0; i < args.get_product().size(); i ++) { + for (u32 i = 0; i < args.get_product().size(); i++) { if (args.get_product()[i].is_error()) return error(); } Value function = callable; if (function.is_intersect()) { map ftypes; - i64 coerced_priority = args.get_product().size() + 1, - exact_priority = coerced_priority * coerced_priority; + i64 coerced_priority = args.get_product().size() + 1, exact_priority = coerced_priority * coerced_priority; // println("args = ", args.type()); // for (const auto& p : function.get_intersect()) { // if (p.first->kind() != KIND_FUNCTION) continue; @@ -1550,10 +1542,9 @@ namespace basil { if (fnargst->count() != args.get_product().size()) continue; i64 priority = 0; const ProductType* argst = (const ProductType*)args.type(); - for (u32 i = 0; i < argst->count(); i ++) { - if (argst->member(i) == fnargst->member(i)) - priority += exact_priority; - else if (fnargst->member(i) != ANY && argst->member(i)->coerces_to(fnargst->member(i))) + for (u32 i = 0; i < argst->count(); i++) { + if (argst->member(i) == fnargst->member(i)) priority += exact_priority; + else if (fnargst->member(i) != ANY && argst->member(i)->coerces_to(fnargst->member(i))) priority += coerced_priority; else if (fnargst->member(i) != ANY) // implies argst[i] cannot coerce to desired type, incompatible goto end; @@ -1568,11 +1559,13 @@ namespace basil { } if (ftypes.size() > 1) { i64 max = 0; - for (const auto& p : ftypes) if (p.second > max) max = p.second; + for (const auto& p : ftypes) + if (p.second > max) max = p.second; set to_remove; - for (const auto& p : ftypes) if (p.second < max) to_remove.insert(p.first); + for (const auto& p : ftypes) + if (p.second < max) to_remove.insert(p.first); for (const FunctionType* t : to_remove) ftypes.erase(t); - + if (ftypes.size() > 1) { err(function.loc(), "Call to '", function, "' is ambiguous; multiple overloads match arguments ", args.type(), "."); @@ -1588,8 +1581,7 @@ namespace basil { err(function.loc(), "Cannot call non-function value '", function, "'."); return error(); } - } - else if (!function.is_function()) { + } else if (!function.is_function()) { err(function.loc(), "Cannot call non-function value '", function, "'."); return error(); } @@ -1598,9 +1590,9 @@ namespace basil { return error(); } - const FunctionType* ft = function.is_runtime() ? - ((const FunctionType*)((const RuntimeType*)function.type())->base()) - : (const FunctionType*)function.type(); + const FunctionType* ft = function.is_runtime() + ? ((const FunctionType*)((const RuntimeType*)function.type())->base()) + : (const FunctionType*)function.type(); const ProductType* argst = (const ProductType*)ft->arg(); if (args.get_product().size() != argst->count()) { @@ -1610,20 +1602,19 @@ namespace basil { } bool has_runtime = false; - for (u32 i = 0; i < argst->count(); i ++) { + for (u32 i = 0; i < argst->count(); i++) { if (args.get_product()[i].is_runtime()) { has_runtime = true; break; } } - + if (function.is_runtime()) has_runtime = true; if (function.is_function()) { FunctionValue& fn = function.get_function(); if (fn.is_builtin()) { if (fn.get_builtin().runtime_only()) has_runtime = true; - } - else { + } else { if (ft->ret()->kind() == KIND_RUNTIME) has_runtime = true; if (!fn.found_calls()) { set visited; @@ -1636,9 +1627,10 @@ namespace basil { const ProductType* rtargst = argst; if (has_runtime) { vector argts; - for (u32 i = 0; i < argst->count(); i ++) { + for (u32 i = 0; i < argst->count(); i++) { if (argst->member(i)->kind() != KIND_RUNTIME) argts.push(find(argst->member(i))); - else argts.push(argst->member(i)); + else + argts.push(argst->member(i)); } rtargst = (const ProductType*)find(argts); } @@ -1652,9 +1644,10 @@ namespace basil { ": expected '", rtargst->member(i), "', given '", argt, "'."); return error(); } - if (argt != rtargst->member(i)) args_copy.get_product()[i] = cast(args.get_product()[i], rtargst->member(i)); + if (argt != rtargst->member(i)) + args_copy.get_product()[i] = cast(args.get_product()[i], rtargst->member(i)); } - for (u32 i = 0; i < args_copy.get_product().size(); i ++) { + for (u32 i = 0; i < args_copy.get_product().size(); i++) { if (args_copy.get_product()[i].is_error()) return error(); } @@ -1671,10 +1664,9 @@ namespace basil { FunctionValue& fn = function.get_function(); if (fn.is_builtin()) { - if (has_runtime && fn.get_builtin().should_lower()) + if (has_runtime && fn.get_builtin().should_lower()) for (Value& v : args_copy.get_product()) v = lower(v); - return has_runtime ? fn.get_builtin().compile(env, args_copy) - : fn.get_builtin().eval(env, args_copy); + return has_runtime ? fn.get_builtin().compile(env, args_copy) : fn.get_builtin().eval(env, args_copy); } else { vector rtargs; ref fnenv = fn.get_env(); @@ -1695,8 +1687,7 @@ namespace basil { ASTNode* n = v.get_runtime(); n->inc(); rtargs.push(n); - } - else { + } else { Def* def = fnenv->find(argname); bindings[argname] = def->value; def->value = args_copy.get_product()[i]; @@ -1711,8 +1702,7 @@ namespace basil { } if (!body) return error(); return new ASTCall(callable.loc(), body, rtargs); - } - else { + } else { Value result = eval(fnenv, fn.body()); for (auto& p : bindings) fnenv->find(p.first)->value = p.second; return result; diff --git a/compiler/values.h b/compiler/values.h index 0348ed2..d505093 100644 --- a/compiler/values.h +++ b/compiler/values.h @@ -1,394 +1,402 @@ #ifndef BASIL_VALUES_H #define BASIL_VALUES_H -#include "util/defs.h" +#include "errors.h" #include "type.h" -#include "util/io.h" +#include "util/defs.h" #include "util/hash.h" -#include "errors.h" -#include "util/vec.h" +#include "util/io.h" #include "util/rc.h" +#include "util/vec.h" namespace basil { - class Def; - class Env; - class ASTNode; - class Builtin; - - u64 symbol_value(const string& symbol); - const string& symbol_for(u64 value); - - class ListValue; - class SumValue; - class IntersectValue; - class ProductValue; - class ArrayValue; - class FunctionValue; - class AliasValue; - class MacroValue; - class NamedValue; - class ModuleValue; - class DictValue; - - class Value { - const Type* _type; - union { - i64 i; - u64 u; - double f; - const Type* t; - bool b; - RC* rc; - } _data; - SourceLocation _loc; - public: - Value(); - Value(const Type* type); - Value(i64 i, const Type* type = INT); - // Value(double d); - Value(const string& s, const Type* type = SYMBOL); - Value(const Type* type_value, const Type* type); - Value(ListValue* l); - Value(SumValue* s, const Type* type); - Value(IntersectValue* i, const Type* type); - Value(ProductValue* p); - Value(ArrayValue* a); - Value(ArrayValue* a, const Type* type); - Value(DictValue* d); - Value(ref env, const Builtin& b); - Value(FunctionValue* f, const Type* ftype); - Value(AliasValue* f); - Value(MacroValue* f); - Value(NamedValue* n, const Type* t); - Value(ModuleValue* m); - Value(ASTNode* n); - ~Value(); - Value(const Value& other); - Value& operator=(const Value& other); - - bool is_int() const; - i64 get_int() const; - i64& get_int(); - - bool is_float() const; - double get_float() const; - double& get_float(); - - bool is_symbol() const; - u64 get_symbol() const; - u64& get_symbol(); - - bool is_string() const; - const string& get_string() const; - string& get_string(); - - bool is_void() const; - - bool is_error() const; - - bool is_type() const; - const Type* get_type() const; - const Type*& get_type(); - - bool is_bool() const; - bool get_bool() const; - bool& get_bool(); - - bool is_list() const; - const ListValue& get_list() const; - ListValue& get_list(); - - bool is_array() const; - const ArrayValue& get_array() const; - ArrayValue& get_array(); - - bool is_sum() const; - const SumValue& get_sum() const; - SumValue& get_sum(); - - bool is_intersect() const; - const IntersectValue& get_intersect() const; - IntersectValue& get_intersect(); - - bool is_product() const; - const ProductValue& get_product() const; - ProductValue& get_product(); - - bool is_dict() const; - const DictValue& get_dict() const; - DictValue& get_dict(); - - bool is_function() const; - const FunctionValue& get_function() const; - FunctionValue& get_function(); - - bool is_alias() const; - const AliasValue& get_alias() const; - AliasValue& get_alias(); - - bool is_macro() const; - const MacroValue& get_macro() const; - MacroValue& get_macro(); - - bool is_runtime() const; - ASTNode* get_runtime() const; - ASTNode*& get_runtime(); - - bool is_named() const; - const NamedValue& get_named() const; - NamedValue& get_named(); - - bool is_module() const; - const ModuleValue& get_module() const; - ModuleValue& get_module(); - - const Type* type() const; - void format(stream& io) const; - u64 hash() const; - bool operator==(const Value& other) const; - bool operator!=(const Value& other) const; - Value clone() const; - - void set_location(SourceLocation loc); - SourceLocation loc() const; - }; - - class StringValue : public RC { - string _value; - public: - StringValue(const string& value); - - string& value(); - const string& value() const; - }; - - class NamedValue : public RC { - Value _inner; - public: - NamedValue(const Value& inner); - - Value& get(); - const Value& get() const; - }; - - class ListValue : public RC { - Value _head, _tail; - public: - ListValue(const Value& head, const Value& tail); - - Value& head(); - const Value& head() const; - Value& tail(); - const Value& tail() const; - }; - - class SumValue : public RC { - Value _value; - public: - SumValue(const Value& value); - - Value& value(); - const Value& value() const; - }; - - class IntersectValue : public RC { - map _values; - public: - IntersectValue(const map& values); - - u32 size() const; - bool has(const Type* t) const; - map::const_iterator begin() const; - map::const_iterator end() const; - map::iterator begin(); - map::iterator end(); - const map& values() const; - map& values(); - }; - - class ProductValue : public RC { - vector _values; - public: - ProductValue(const vector& values); - - u32 size() const; - Value& operator[](u32 i); - const Value& operator[](u32 i) const; - const Value* begin() const; - const Value* end() const; - Value* begin(); - Value* end(); - const vector& values() const; - }; - - class ArrayValue : public ProductValue { - public: - ArrayValue(const vector& values); - }; - - class DictValue : public RC { - map _entries; - public: - DictValue(const map& entries); - - u32 size() const; - Value* operator[](const Value& key); - const Value* operator[](const Value& key) const; - const map::const_iterator begin() const; - const map::const_iterator end() const; - map::iterator begin(); - map::iterator end(); - const map& entries() const; - }; - - using BuiltinFn = Value (*)(ref, const Value& params); - - extern const u64 KEYWORD_ARG_BIT; - extern const u64 ARG_NAME_MASK; - - class FunctionValue : public RC { - i64 _name; - Value _code; - const Builtin* _builtin; - ref _env; - vector _args; - set* _calls; - map* _insts; - public: - FunctionValue(ref env, const vector& args, - const Value& code, i64 name = -1); - FunctionValue(ref env, const Builtin& builtin, i64 name = -1); - ~FunctionValue(); - FunctionValue(const FunctionValue& other); - FunctionValue& operator=(const FunctionValue& other); - - const vector& args() const; - const Value& body() const; - bool is_builtin() const; - u64 arity() const; - const Builtin& get_builtin() const; - ref get_env(); - const ref get_env() const; - i64 name() const; - bool found_calls() const; - bool recursive() const; - void add_call(const FunctionValue* other); - ASTNode* instantiation(const Type* args) const; - const map* instantiations() const; - void instantiate(const Type* args, ASTNode* body); - }; - - class AliasValue : public RC { - Value _value; - public: - AliasValue(const Value& value); - - Value& value(); - const Value& value() const; - }; - - using BuiltinMacro = Value (*)(ref, const Value& params); - - class MacroValue : public RC { - Value _code; - const Builtin* _builtin; - ref _env; - vector _args; - public: - MacroValue(ref env, const vector& args, - const Value& code); - MacroValue(ref env, const Builtin& builtin); - - const vector& args() const; - const Value& body() const; - bool is_builtin() const; - u64 arity() const; - const Builtin& get_builtin() const; - ref get_env(); - const ref get_env() const; - }; - - class ModuleValue : public RC { - map _entries; - public: - ModuleValue(const map& entries); - - const map& entries() const; - bool has(u64 name) const; - const Value& entry(u64 name) const; - }; - - Value lower(const Value& v); - - Value add(const Value& lhs, const Value& rhs); - Value sub(const Value& lhs, const Value& rhs); - Value mul(const Value& lhs, const Value& rhs); - Value div(const Value& lhs, const Value& rhs); - Value rem(const Value& lhs, const Value& rhs); - - Value logical_and(const Value& lhs, const Value& rhs); - Value logical_or(const Value& lhs, const Value& rhs); - Value logical_xor(const Value& lhs, const Value& rhs); - Value logical_not(const Value& v); - - Value equal(const Value& lhs, const Value& rhs); - Value inequal(const Value& lhs, const Value& rhs); - Value less(const Value& lhs, const Value& rhs); - Value greater(const Value& lhs, const Value& rhs); - Value less_equal(const Value& lhs, const Value& rhs); - Value greater_equal(const Value& lhs, const Value& rhs); - - Value head(const Value& v); - Value tail(const Value& v); - Value cons(const Value& head, const Value& tail); - Value empty(); - Value at(const Value& tuple, const Value& index); - vector to_vector(const Value& list); - Value is_empty(const Value& list); - - Value list_of(const Value& element); - template - Value list_of(const Value& element, Args... args) { - return cons(element, list_of(args...)); - } - Value list_of(const vector& elements); - - template - Value tuple_of(const Value& element, Args... args) { - return tuple_of(vector_of(args...)); - } - Value tuple_of(const vector& elements); - - template - Value array_of(const Value& element, Args... args) { - return array_of(vector_of(args...)); - } - Value array_of(const vector& elements); - - Value dict_of(const map &elements); - - - Value error(); - - Value length(const Value& str); - - Value read_line(); - Value strcat(const Value& a, const Value& b); - Value substr(const Value& str, const Value& start, const Value& end); - - ASTNode* instantiate(SourceLocation loc, FunctionValue& fn, - const Type* args_type); - Value type_of(const Value& v); - Value is(const Value& v, const Value& t); - Value cast(const Value& val, const Type* type); - Value annotate(const Value& val, const Value& type); - Value as(const Value& v, const Value& t); - Value call(ref env, Value& function, const Value& arg); - Value display(const Value& arg); - Value assign(ref env, const Value& dest, const Value& src); -} - -template<> + class Def; + class Env; + class ASTNode; + class Builtin; + + u64 symbol_value(const string& symbol); + const string& symbol_for(u64 value); + + class ListValue; + class SumValue; + class IntersectValue; + class ProductValue; + class ArrayValue; + class FunctionValue; + class AliasValue; + class MacroValue; + class NamedValue; + class ModuleValue; + class DictValue; + + class Value { + const Type* _type; + union { + i64 i; + u64 u; + double f; + const Type* t; + bool b; + RC* rc; + } _data; + SourceLocation _loc; + + public: + Value(); + Value(const Type* type); + Value(i64 i, const Type* type = INT); + // Value(double d); + Value(const string& s, const Type* type = SYMBOL); + Value(const Type* type_value, const Type* type); + Value(ListValue* l); + Value(SumValue* s, const Type* type); + Value(IntersectValue* i, const Type* type); + Value(ProductValue* p); + Value(ArrayValue* a); + Value(ArrayValue* a, const Type* type); + Value(DictValue* d); + Value(ref env, const Builtin& b); + Value(FunctionValue* f, const Type* ftype); + Value(AliasValue* f); + Value(MacroValue* f); + Value(NamedValue* n, const Type* t); + Value(ModuleValue* m); + Value(ASTNode* n); + ~Value(); + Value(const Value& other); + Value& operator=(const Value& other); + + bool is_int() const; + i64 get_int() const; + i64& get_int(); + + bool is_float() const; + double get_float() const; + double& get_float(); + + bool is_symbol() const; + u64 get_symbol() const; + u64& get_symbol(); + + bool is_string() const; + const string& get_string() const; + string& get_string(); + + bool is_void() const; + + bool is_error() const; + + bool is_type() const; + const Type* get_type() const; + const Type*& get_type(); + + bool is_bool() const; + bool get_bool() const; + bool& get_bool(); + + bool is_list() const; + const ListValue& get_list() const; + ListValue& get_list(); + + bool is_array() const; + const ArrayValue& get_array() const; + ArrayValue& get_array(); + + bool is_sum() const; + const SumValue& get_sum() const; + SumValue& get_sum(); + + bool is_intersect() const; + const IntersectValue& get_intersect() const; + IntersectValue& get_intersect(); + + bool is_product() const; + const ProductValue& get_product() const; + ProductValue& get_product(); + + bool is_dict() const; + const DictValue& get_dict() const; + DictValue& get_dict(); + + bool is_function() const; + const FunctionValue& get_function() const; + FunctionValue& get_function(); + + bool is_alias() const; + const AliasValue& get_alias() const; + AliasValue& get_alias(); + + bool is_macro() const; + const MacroValue& get_macro() const; + MacroValue& get_macro(); + + bool is_runtime() const; + ASTNode* get_runtime() const; + ASTNode*& get_runtime(); + + bool is_named() const; + const NamedValue& get_named() const; + NamedValue& get_named(); + + bool is_module() const; + const ModuleValue& get_module() const; + ModuleValue& get_module(); + + const Type* type() const; + void format(stream& io) const; + u64 hash() const; + bool operator==(const Value& other) const; + bool operator!=(const Value& other) const; + Value clone() const; + + void set_location(SourceLocation loc); + SourceLocation loc() const; + }; + + class StringValue : public RC { + string _value; + + public: + StringValue(const string& value); + + string& value(); + const string& value() const; + }; + + class NamedValue : public RC { + Value _inner; + + public: + NamedValue(const Value& inner); + + Value& get(); + const Value& get() const; + }; + + class ListValue : public RC { + Value _head, _tail; + + public: + ListValue(const Value& head, const Value& tail); + + Value& head(); + const Value& head() const; + Value& tail(); + const Value& tail() const; + }; + + class SumValue : public RC { + Value _value; + + public: + SumValue(const Value& value); + + Value& value(); + const Value& value() const; + }; + + class IntersectValue : public RC { + map _values; + + public: + IntersectValue(const map& values); + + u32 size() const; + bool has(const Type* t) const; + map::const_iterator begin() const; + map::const_iterator end() const; + map::iterator begin(); + map::iterator end(); + const map& values() const; + map& values(); + }; + + class ProductValue : public RC { + vector _values; + + public: + ProductValue(const vector& values); + + u32 size() const; + Value& operator[](u32 i); + const Value& operator[](u32 i) const; + const Value* begin() const; + const Value* end() const; + Value* begin(); + Value* end(); + const vector& values() const; + }; + + class ArrayValue : public ProductValue { + public: + ArrayValue(const vector& values); + }; + + class DictValue : public RC { + map _entries; + + public: + DictValue(const map& entries); + + u32 size() const; + Value* operator[](const Value& key); + const Value* operator[](const Value& key) const; + const map::const_iterator begin() const; + const map::const_iterator end() const; + map::iterator begin(); + map::iterator end(); + const map& entries() const; + }; + + using BuiltinFn = Value (*)(ref, const Value& params); + + extern const u64 KEYWORD_ARG_BIT; + extern const u64 ARG_NAME_MASK; + + class FunctionValue : public RC { + i64 _name; + Value _code; + const Builtin* _builtin; + ref _env; + vector _args; + set* _calls; + map* _insts; + + public: + FunctionValue(ref env, const vector& args, const Value& code, i64 name = -1); + FunctionValue(ref env, const Builtin& builtin, i64 name = -1); + ~FunctionValue(); + FunctionValue(const FunctionValue& other); + FunctionValue& operator=(const FunctionValue& other); + + const vector& args() const; + const Value& body() const; + bool is_builtin() const; + u64 arity() const; + const Builtin& get_builtin() const; + ref get_env(); + const ref get_env() const; + i64 name() const; + bool found_calls() const; + bool recursive() const; + void add_call(const FunctionValue* other); + ASTNode* instantiation(const Type* args) const; + const map* instantiations() const; + void instantiate(const Type* args, ASTNode* body); + }; + + class AliasValue : public RC { + Value _value; + + public: + AliasValue(const Value& value); + + Value& value(); + const Value& value() const; + }; + + using BuiltinMacro = Value (*)(ref, const Value& params); + + class MacroValue : public RC { + Value _code; + const Builtin* _builtin; + ref _env; + vector _args; + + public: + MacroValue(ref env, const vector& args, const Value& code); + MacroValue(ref env, const Builtin& builtin); + + const vector& args() const; + const Value& body() const; + bool is_builtin() const; + u64 arity() const; + const Builtin& get_builtin() const; + ref get_env(); + const ref get_env() const; + }; + + class ModuleValue : public RC { + map _entries; + + public: + ModuleValue(const map& entries); + + const map& entries() const; + bool has(u64 name) const; + const Value& entry(u64 name) const; + }; + + Value lower(const Value& v); + + Value add(const Value& lhs, const Value& rhs); + Value sub(const Value& lhs, const Value& rhs); + Value mul(const Value& lhs, const Value& rhs); + Value div(const Value& lhs, const Value& rhs); + Value rem(const Value& lhs, const Value& rhs); + + Value logical_and(const Value& lhs, const Value& rhs); + Value logical_or(const Value& lhs, const Value& rhs); + Value logical_xor(const Value& lhs, const Value& rhs); + Value logical_not(const Value& v); + + Value equal(const Value& lhs, const Value& rhs); + Value inequal(const Value& lhs, const Value& rhs); + Value less(const Value& lhs, const Value& rhs); + Value greater(const Value& lhs, const Value& rhs); + Value less_equal(const Value& lhs, const Value& rhs); + Value greater_equal(const Value& lhs, const Value& rhs); + + Value head(const Value& v); + Value tail(const Value& v); + Value cons(const Value& head, const Value& tail); + Value empty(); + Value at(const Value& tuple, const Value& index); + vector to_vector(const Value& list); + Value is_empty(const Value& list); + + Value list_of(const Value& element); + template + Value list_of(const Value& element, Args... args) { + return cons(element, list_of(args...)); + } + Value list_of(const vector& elements); + + template + Value tuple_of(const Value& element, Args... args) { + return tuple_of(vector_of(args...)); + } + Value tuple_of(const vector& elements); + + template + Value array_of(const Value& element, Args... args) { + return array_of(vector_of(args...)); + } + Value array_of(const vector& elements); + + Value dict_of(const map& elements); + + Value error(); + + Value length(const Value& str); + + Value read_line(); + Value strcat(const Value& a, const Value& b); + Value substr(const Value& str, const Value& start, const Value& end); + + ASTNode* instantiate(SourceLocation loc, FunctionValue& fn, const Type* args_type); + Value type_of(const Value& v); + Value is(const Value& v, const Value& t); + Value cast(const Value& val, const Type* type); + Value annotate(const Value& val, const Value& type); + Value as(const Value& v, const Value& t); + Value call(ref env, Value& function, const Value& arg); + Value display(const Value& arg); + Value assign(ref env, const Value& dest, const Value& src); +} // namespace basil + +template <> u64 hash(const basil::Value& t); void write(stream& io, const basil::Value& t); From f840479bc314e660171a3eb91c404f37c4be4a33 Mon Sep 17 00:00:00 2001 From: Alec Minchington Date: Fri, 19 Feb 2021 00:26:06 -0500 Subject: [PATCH 15/17] Minor .clang-format change --- .clang-format | 3 + compiler/builtin.cpp | 343 ++++++++++++++++++++++--------------------- 2 files changed, 178 insertions(+), 168 deletions(-) diff --git a/.clang-format b/.clang-format index 2dfa0b9..fd913bd 100644 --- a/.clang-format +++ b/.clang-format @@ -1,6 +1,8 @@ BasedOnStyle: LLVM IndentWidth: 4 ColumnLimit: 120 + +AllowAllArgumentsOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: true AllowShortCaseLabelsOnASingleLine: true @@ -8,6 +10,7 @@ AllowShortFunctionsOnASingleLine: Empty AllowShortIfStatementsOnASingleLine: Always AllowShortLoopsOnASingleLine: true AlwaysBreakTemplateDeclarations: Yes +Cpp11BracedListStyle: false FixNamespaceComments: true IndentCaseLabels: true IndentPPDirectives: BeforeHash diff --git a/compiler/builtin.cpp b/compiler/builtin.cpp index 4171196..74a20ad 100644 --- a/compiler/builtin.cpp +++ b/compiler/builtin.cpp @@ -46,114 +46,119 @@ namespace basil { STRCAT, SUBSTR, ANNOTATE, TYPEOF, TYPEDEF, LIST_TYPE, OF_TYPE_MACRO, OF_TYPE, ASSIGN, IF; static void init_builtins() { - ADD_INT = {find(find(INT, INT), INT), - [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() + ARG(1).get_int()); }, - [](ref env, const Value& args) -> Value { - return Value( - new ASTBinaryMath(ARG(0).loc(), AST_ADD, ARG(0).get_runtime(), ARG(1).get_runtime())); - }}; - ADD_SYMBOL = {find(find(SYMBOL, SYMBOL), SYMBOL), - [](ref env, const Value& args) -> Value { - return Value(symbol_for(ARG(0).get_symbol()) + symbol_for(ARG(1).get_symbol()), SYMBOL); - }, - nullptr}; - SUB = {find(find(INT, INT), INT), - [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() - ARG(1).get_int()); }, - [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryMath(ARG(0).loc(), AST_SUB, ARG(0).get_runtime(), ARG(1).get_runtime())); - }}; - MUL = {find(find(INT, INT), INT), - [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() * ARG(1).get_int()); }, - [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryMath(ARG(0).loc(), AST_MUL, ARG(0).get_runtime(), ARG(1).get_runtime())); - }}; - DIV = {find(find(INT, INT), INT), - [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() / ARG(1).get_int()); }, - [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryMath(ARG(0).loc(), AST_DIV, ARG(0).get_runtime(), ARG(1).get_runtime())); - }}; - REM = {find(find(INT, INT), INT), - [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() % ARG(1).get_int()); }, - [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryMath(ARG(0).loc(), AST_REM, ARG(0).get_runtime(), ARG(1).get_runtime())); - }}; - AND = {find(find(BOOL, BOOL), BOOL), + ADD_INT = { find(find(INT, INT), INT), + [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() + ARG(1).get_int()); }, + [](ref env, const Value& args) -> Value { + return Value( + new ASTBinaryMath(ARG(0).loc(), AST_ADD, ARG(0).get_runtime(), ARG(1).get_runtime())); + } }; + ADD_SYMBOL = { find(find(SYMBOL, SYMBOL), SYMBOL), + [](ref env, const Value& args) -> Value { + return Value(symbol_for(ARG(0).get_symbol()) + symbol_for(ARG(1).get_symbol()), SYMBOL); + }, + nullptr }; + SUB = { find(find(INT, INT), INT), + [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() - ARG(1).get_int()); }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryMath(ARG(0).loc(), AST_SUB, ARG(0).get_runtime(), ARG(1).get_runtime())); + } }; + MUL = { find(find(INT, INT), INT), + [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() * ARG(1).get_int()); }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryMath(ARG(0).loc(), AST_MUL, ARG(0).get_runtime(), ARG(1).get_runtime())); + } }; + DIV = { find(find(INT, INT), INT), + [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() / ARG(1).get_int()); }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryMath(ARG(0).loc(), AST_DIV, ARG(0).get_runtime(), ARG(1).get_runtime())); + } }; + REM = { find(find(INT, INT), INT), + [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() % ARG(1).get_int()); }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryMath(ARG(0).loc(), AST_REM, ARG(0).get_runtime(), ARG(1).get_runtime())); + } }; + AND = { find(find(BOOL, BOOL), BOOL), + [](ref env, const Value& args) -> Value { + return Value(ARG(0).get_bool() && ARG(1).get_bool(), BOOL); + }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryLogic(ARG(0).loc(), AST_AND, ARG(0).get_runtime(), ARG(1).get_runtime())); + } }; + OR = { find(find(BOOL, BOOL), BOOL), [](ref env, const Value& args) -> Value { - return Value(ARG(0).get_bool() && ARG(1).get_bool(), BOOL); + return Value(ARG(0).get_bool() || ARG(1).get_bool(), BOOL); }, [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryLogic(ARG(0).loc(), AST_AND, ARG(0).get_runtime(), ARG(1).get_runtime())); - }}; - OR = {find(find(BOOL, BOOL), BOOL), - [](ref env, const Value& args) -> Value { - return Value(ARG(0).get_bool() || ARG(1).get_bool(), BOOL); - }, - [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryLogic(ARG(0).loc(), AST_OR, ARG(0).get_runtime(), ARG(1).get_runtime())); - }}; - XOR = { - find(find(BOOL, BOOL), BOOL), - [](ref env, const Value& args) -> Value { return Value(ARG(0).get_bool() ^ ARG(1).get_bool(), BOOL); }, - [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryLogic(ARG(0).loc(), AST_XOR, ARG(0).get_runtime(), ARG(1).get_runtime())); - }}; - NOT = {find(find(BOOL), BOOL), - [](ref env, const Value& args) -> Value { return Value(!ARG(0).get_bool(), BOOL); }, - [](ref env, const Value& args) -> Value { - return Value(new ASTNot(ARG(0).loc(), ARG(0).get_runtime())); - }}; - EQUALS = {find(find(ANY, ANY), BOOL), - [](ref env, const Value& args) -> Value { return Value(ARG(0) == ARG(1), BOOL); }, - [](ref env, const Value& args) -> Value { - return Value( - new ASTBinaryEqual(ARG(0).loc(), AST_EQUAL, ARG(0).get_runtime(), ARG(1).get_runtime())); - }}; - NOT_EQUALS = { - find(find(ANY, ANY), BOOL), - [](ref env, const Value& args) -> Value { return Value(ARG(0) != ARG(1), BOOL); }, - [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryEqual(ARG(0).loc(), AST_INEQUAL, ARG(0).get_runtime(), ARG(1).get_runtime())); - }}; - LESS = { - find(find(INT, INT), BOOL), - [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() < ARG(1).get_int(), BOOL); }, - [](ref env, const Value& args) -> Value { - return Value(new ASTBinaryRel(ARG(0).loc(), AST_LESS, ARG(0).get_runtime(), ARG(1).get_runtime())); - }}; - LESS_EQUAL = { - find(find(INT, INT), BOOL), - [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() <= ARG(1).get_int(), BOOL); }, - [](ref env, const Value& args) -> Value { - return Value( - new ASTBinaryRel(ARG(0).loc(), AST_LESS_EQUAL, ARG(0).get_runtime(), ARG(1).get_runtime())); - }}; + return Value(new ASTBinaryLogic(ARG(0).loc(), AST_OR, ARG(0).get_runtime(), ARG(1).get_runtime())); + } }; + XOR = { find(find(BOOL, BOOL), BOOL), + [](ref env, const Value& args) -> Value { + return Value(ARG(0).get_bool() ^ ARG(1).get_bool(), BOOL); + }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryLogic(ARG(0).loc(), AST_XOR, ARG(0).get_runtime(), ARG(1).get_runtime())); + } }; + NOT = { find(find(BOOL), BOOL), + [](ref env, const Value& args) -> Value { return Value(!ARG(0).get_bool(), BOOL); }, + [](ref env, const Value& args) -> Value { + return Value(new ASTNot(ARG(0).loc(), ARG(0).get_runtime())); + } }; + EQUALS = { find(find(ANY, ANY), BOOL), + [](ref env, const Value& args) -> Value { return Value(ARG(0) == ARG(1), BOOL); }, + [](ref env, const Value& args) -> Value { + return Value( + new ASTBinaryEqual(ARG(0).loc(), AST_EQUAL, ARG(0).get_runtime(), ARG(1).get_runtime())); + } }; + NOT_EQUALS = { find(find(ANY, ANY), BOOL), + [](ref env, const Value& args) -> Value { return Value(ARG(0) != ARG(1), BOOL); }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryEqual(ARG(0).loc(), AST_INEQUAL, ARG(0).get_runtime(), + ARG(1).get_runtime())); + } }; + LESS = { find(find(INT, INT), BOOL), + [](ref env, const Value& args) -> Value { + return Value(ARG(0).get_int() < ARG(1).get_int(), BOOL); + }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryRel(ARG(0).loc(), AST_LESS, ARG(0).get_runtime(), ARG(1).get_runtime())); + } }; + LESS_EQUAL = { find(find(INT, INT), BOOL), + [](ref env, const Value& args) -> Value { + return Value(ARG(0).get_int() <= ARG(1).get_int(), BOOL); + }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryRel(ARG(0).loc(), AST_LESS_EQUAL, ARG(0).get_runtime(), + ARG(1).get_runtime())); + } }; GREATER = { find(find(INT, INT), BOOL), [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() > ARG(1).get_int(), BOOL); }, [](ref env, const Value& args) -> Value { return Value(new ASTBinaryRel(ARG(0).loc(), AST_GREATER, ARG(0).get_runtime(), ARG(1).get_runtime())); - }}; - GREATER_EQUAL = { - find(find(INT, INT), BOOL), - [](ref env, const Value& args) -> Value { return Value(ARG(0).get_int() >= ARG(1).get_int(), BOOL); }, - [](ref env, const Value& args) -> Value { - return Value( - new ASTBinaryRel(ARG(0).loc(), AST_GREATER_EQUAL, ARG(0).get_runtime(), ARG(1).get_runtime())); - }}; - DICT_IN = {find(find(ANY, find(ANY, ANY)), BOOL), - [](ref env, const Value& args) -> Value { - return Value(ARG(1).get_dict()[ARG(0)] ? true : false, BOOL); - }, - nullptr}; - DICT_NOT_IN = {find(find(ANY, find(ANY, ANY)), BOOL), - [](ref env, const Value& args) -> Value { - return Value(ARG(1).get_dict()[ARG(0)] ? false : true, BOOL); - }, - nullptr}; - DISPLAY = {find(find(ANY), VOID), nullptr, - [](ref env, const Value& args) -> Value { - return Value(new ASTDisplay(ARG(0).loc(), ARG(0).get_runtime())); - }}; + } + }; + GREATER_EQUAL = { find(find(INT, INT), BOOL), + [](ref env, const Value& args) -> Value { + return Value(ARG(0).get_int() >= ARG(1).get_int(), BOOL); + }, + [](ref env, const Value& args) -> Value { + return Value(new ASTBinaryRel(ARG(0).loc(), AST_GREATER_EQUAL, ARG(0).get_runtime(), + ARG(1).get_runtime())); + } }; + DICT_IN = { find(find(ANY, find(ANY, ANY)), BOOL), + [](ref env, const Value& args) -> Value { + return Value(ARG(1).get_dict()[ARG(0)] ? true : false, BOOL); + }, + nullptr }; + DICT_NOT_IN = { find(find(ANY, find(ANY, ANY)), BOOL), + [](ref env, const Value& args) -> Value { + return Value(ARG(1).get_dict()[ARG(0)] ? false : true, BOOL); + }, + nullptr }; + DISPLAY = { find(find(ANY), VOID), nullptr, + [](ref env, const Value& args) -> Value { + return Value(new ASTDisplay(ARG(0).loc(), ARG(0).get_runtime())); + } }; AT_INT = { find(find(ANY, INT), ANY), [](ref env, const Value& args) -> Value { @@ -200,84 +205,86 @@ namespace basil { }, nullptr // todo: runtime indexing }; - AT_ARRAY_TYPE = {find(find(TYPE, INT), TYPE), + AT_ARRAY_TYPE = { find(find(TYPE, INT), TYPE), + [](ref env, const Value& args) -> Value { + return Value(find(ARG(0).get_type(), ARG(1).get_int()), TYPE); + }, + nullptr }; + AT_DYNARRAY_TYPE = { find(find(TYPE, VOID), TYPE), + [](ref env, const Value& args) -> Value { + return Value(find(ARG(0).get_type()), TYPE); + }, + nullptr }; + AT_DICT = { find(find(find(ANY, ANY), ANY), ANY), + [](ref env, const Value& args) -> Value { return *(ARG(0).get_dict())[ARG(1)]; }, nullptr }; + AT_DICT_LIST = { find(find(find(ANY, ANY), find(ANY)), ANY), [](ref env, const Value& args) -> Value { - return Value(find(ARG(0).get_type(), ARG(1).get_int()), TYPE); + vector vals; + for (const Value& key : to_vector(ARG(1))) vals.push(*ARG(0).get_dict()[key]); + return Value(new ArrayValue(vals)); }, - nullptr}; - AT_DYNARRAY_TYPE = { - find(find(TYPE, VOID), TYPE), - [](ref env, const Value& args) -> Value { return Value(find(ARG(0).get_type()), TYPE); }, - nullptr}; - AT_DICT = {find(find(find(ANY, ANY), ANY), ANY), - [](ref env, const Value& args) -> Value { return *(ARG(0).get_dict())[ARG(1)]; }, nullptr}; - AT_DICT_LIST = {find(find(find(ANY, ANY), find(ANY)), ANY), - [](ref env, const Value& args) -> Value { - vector vals; - for (const Value& key : to_vector(ARG(1))) vals.push(*ARG(0).get_dict()[key]); - return Value(new ArrayValue(vals)); - }, - nullptr}; - AT_DICT_TYPE = {find(find(TYPE, TYPE), TYPE), - [](ref env, const Value& args) -> Value { - return Value(find(ARG(1).get_type(), ARG(0).get_type()), TYPE); - }, - nullptr}; - AT_MODULE = {find(find(MODULE, SYMBOL), ANY), + nullptr }; + AT_DICT_TYPE = { find(find(TYPE, TYPE), TYPE), + [](ref env, const Value& args) -> Value { + return Value(find(ARG(1).get_type(), ARG(0).get_type()), TYPE); + }, + nullptr }; + AT_MODULE = { find(find(MODULE, SYMBOL), ANY), + [](ref env, const Value& args) -> Value { + if (!ARG(0).get_module().has(ARG(1).get_symbol())) { + err(ARG(1).loc(), "Module does not contain member '", symbol_for(ARG(1).get_symbol()), + "'."); + return error(); + } + return ARG(0).get_module().entry(ARG(1).get_symbol()); + }, + nullptr }; + ANNOTATE = { find(find(ANY, TYPE), ANY), [](ref env, const Value& args) -> Value { - if (!ARG(0).get_module().has(ARG(1).get_symbol())) { - err(ARG(1).loc(), "Module does not contain member '", symbol_for(ARG(1).get_symbol()), - "'."); + if (!ARG(0).type()->coerces_to(ARG(1).get_type())) { + err(ARG(0).loc(), "Could not unify value of type '", ARG(0).type(), "' with type '", + ARG(1).get_type(), "'."); return error(); } - return ARG(0).get_module().entry(ARG(1).get_symbol()); + return cast(ARG(0), ARG(1).get_type()); }, - nullptr}; - ANNOTATE = {find(find(ANY, TYPE), ANY), + [](ref env, const Value& args) -> Value { + return new ASTAnnotate(ARG(0).loc(), lower(ARG(0)).get_runtime(), ARG(1).get_type()); + }, + NO_AUTO_LOWER }; + TYPEOF = { find(find(ANY), TYPE), + [](ref env, const Value& args) -> Value { return Value(ARG(0).type(), TYPE); }, nullptr }; + TYPEDEF = { find(2), [](ref env, const Value& args) -> Value { - if (!ARG(0).type()->coerces_to(ARG(1).get_type())) { - err(ARG(0).loc(), "Could not unify value of type '", ARG(0).type(), "' with type '", - ARG(1).get_type(), "'."); - return error(); - } - return cast(ARG(0), ARG(1).get_type()); + return list_of(Value("def", SYMBOL), ARG(0), + list_of(Value("#of", SYMBOL), list_of(Value("quote", SYMBOL), ARG(0)), ARG(1))); }, + nullptr }; + LIST_TYPE = { find(find(TYPE), TYPE), + [](ref env, const Value& args) -> Value { + return Value(find(ARG(0).get_type()), TYPE); + }, + nullptr }; + OF_TYPE_MACRO = { find(2), + [](ref env, const Value& args) -> Value { + return list_of(Value("#of", SYMBOL), list_of(Value("quote", SYMBOL), ARG(0)), ARG(1)); + }, + nullptr }; + OF_TYPE = { find(find(SYMBOL, TYPE), TYPE), [](ref env, const Value& args) -> Value { - return new ASTAnnotate(ARG(0).loc(), lower(ARG(0)).get_runtime(), ARG(1).get_type()); + return Value(find(symbol_for(ARG(0).get_symbol()), ARG(1).get_type()), TYPE); }, - NO_AUTO_LOWER}; - TYPEOF = {find(find(ANY), TYPE), - [](ref env, const Value& args) -> Value { return Value(ARG(0).type(), TYPE); }, nullptr}; - TYPEDEF = {find(2), - [](ref env, const Value& args) -> Value { - return list_of(Value("def", SYMBOL), ARG(0), - list_of(Value("#of", SYMBOL), list_of(Value("quote", SYMBOL), ARG(0)), ARG(1))); - }, - nullptr}; - LIST_TYPE = { - find(find(TYPE), TYPE), - [](ref env, const Value& args) -> Value { return Value(find(ARG(0).get_type()), TYPE); }, - nullptr}; - OF_TYPE_MACRO = {find(2), - [](ref env, const Value& args) -> Value { - return list_of(Value("#of", SYMBOL), list_of(Value("quote", SYMBOL), ARG(0)), ARG(1)); - }, - nullptr}; - OF_TYPE = {find(find(SYMBOL, TYPE), TYPE), - [](ref env, const Value& args) -> Value { - return Value(find(symbol_for(ARG(0).get_symbol()), ARG(1).get_type()), TYPE); - }, - nullptr}; - IF = {find(find(BOOL, ANY, ANY), ANY), - [](ref env, const Value& args) -> Value { return eval(env, ARG(ARG(0).get_bool() ? 1 : 2)); }, - [](ref env, const Value& args) -> Value { - Value left = eval(env, ARG(1)), right = eval(env, ARG(2)); - if (left.is_error() || right.is_error()) return error(); - if (!left.is_runtime()) left = lower(left); - if (!right.is_runtime()) right = lower(right); - return new ASTIf(ARG(0).loc(), ARG(0).get_runtime(), left.get_runtime(), right.get_runtime()); - }, - NO_AUTO_LOWER}; + nullptr }; + IF = { find(find(BOOL, ANY, ANY), ANY), + [](ref env, const Value& args) -> Value { return eval(env, ARG(ARG(0).get_bool() ? 1 : 2)); }, + [](ref env, const Value& args) -> Value { + Value left = eval(env, ARG(1)), right = eval(env, ARG(2)); + if (left.is_error() || right.is_error()) return error(); + if (!left.is_runtime()) left = lower(left); + if (!right.is_runtime()) right = lower(right); + return new ASTIf(ARG(0).loc(), ARG(0).get_runtime(), left.get_runtime(), right.get_runtime()); + }, + NO_AUTO_LOWER }; } static bool inited = false; From 84bfca212395f2b07e199010f12564dafbf28461 Mon Sep 17 00:00:00 2001 From: elucent <9532786+elucent@users.noreply.github.com> Date: Fri, 9 Apr 2021 01:15:56 -0400 Subject: [PATCH 16/17] WIP changes to function call syntax, among other changes. --- compiler/builtin.cpp | 113 +++++++--- compiler/builtin.h | 3 +- compiler/driver.cpp | 5 +- compiler/env.cpp | 130 ++++++++---- compiler/env.h | 82 ++++++-- compiler/errors.cpp | 61 ++++-- compiler/errors.h | 14 +- compiler/eval.cpp | 487 +++++++++++++++++++++++-------------------- compiler/main.cpp | 40 ++-- compiler/parse.cpp | 80 +++---- compiler/parse.h | 2 +- compiler/values.cpp | 99 +++++++-- compiler/values.h | 5 +- std/math.bl | 18 +- test/defs.bl | 28 +++ test/rational.bl | 24 --- 16 files changed, 749 insertions(+), 442 deletions(-) create mode 100644 test/defs.bl delete mode 100644 test/rational.bl diff --git a/compiler/builtin.cpp b/compiler/builtin.cpp index 9eff589..ccf08aa 100644 --- a/compiler/builtin.cpp +++ b/compiler/builtin.cpp @@ -47,7 +47,8 @@ namespace basil { DISPLAY, READ_LINE, READ_WORD, READ_INT, LENGTH, AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, AT_MODULE, AT_DICT, AT_DICT_TYPE, AT_DICT_LIST, STRCAT, SUBSTR, ANNOTATE, TYPEOF, LIST_TYPE, OF_TYPE_MACRO, - OF_TYPE, ASSIGN, IF; + OF_TYPE, ASSIGN, IF, + EVAL, FLAG; static void init_builtins() { ADD_INT = {find(find(INT, INT), INT), @@ -311,6 +312,23 @@ namespace basil { }, NO_AUTO_LOWER }; + EVAL = { + find(find(ANY), ANY), + [](ref env, const Value& args) -> Value { + Value term = ARG(0); + prep(env, term); + return eval(env, term); + }, + nullptr + }; + FLAG = { + find(find(ANY), VOID), + [](ref env, const Value& args) -> Value { + err(ARG(0).loc(), "Flagged!"); + return empty(); + }, + nullptr + }; } static bool inited = false; @@ -324,33 +342,76 @@ namespace basil { return Value(new IntersectValue(m), find(ts)); } + template + Value cases(ref env, const char* name, const Args... args) { + vector vals = vector_of(args...); + set ts; + map m; + for (Builtin* v : vals) ts.insert(v->type()), m.put(v->type(), Value(env, *v)); + for (auto& e : m) e.second.set_name(name); + Value result = Value(new IntersectValue(m), find(ts)); + result.set_name(name); + return result; + } + void define_builtins(ref env) { if (!inited) inited = true, init_builtins(); - env->infix("+", cases(env, &ADD_INT, &ADD_SYMBOL), 2, 20); - env->infix("-", Value(env, SUB), 2, 20); - env->infix("*", Value(env, MUL), 2, 40); - env->infix("/", Value(env, DIV), 2, 40); - env->infix("%", Value(env, REM), 2, 40); - env->infix("and", Value(env, AND), 2, 5); - env->infix("or", Value(env, OR), 2, 5); - env->infix("xor", Value(env, XOR), 2, 5); - env->def("not", Value(env, NOT), 1); - env->infix("==", Value(env, EQUALS), 2, 10); - env->infix("!=", Value(env, NOT_EQUALS), 2, 10); - env->infix("<", Value(env, LESS), 2, 10); - env->infix(">", Value(env, GREATER), 2, 10); - env->infix("<=", Value(env, LESS_EQUAL), 2, 10); - env->infix(">=", Value(env, GREATER_EQUAL), 2, 10); - env->infix("in", Value(env, DICT_IN), 2, 60); + env->func("+", Proto::overloaded( + Proto::of(ARG_VARIABLE, "+", ARG_VARIABLE), + Proto::of("+", ARG_VARIABLE, ARG_VARIABLE) + ), + cases(env, "+", &ADD_INT, &ADD_SYMBOL), 20); + env->func("*", Proto::of(ARG_VARIABLE, "*", ARG_VARIABLE), + Value(env, MUL, "*"), 40); + env->func("-", Proto::of(ARG_VARIABLE, "-", ARG_VARIABLE), + Value(env, SUB, "-"), 20); + env->func("/", Proto::of(ARG_VARIABLE, "/", ARG_VARIABLE), + Value(env, DIV, "/"), 40); + env->func("%", Proto::of(ARG_VARIABLE, "%", ARG_VARIABLE), + Value(env, REM, "%"), 40); + env->func("and", Proto::of(ARG_VARIABLE, "and", ARG_VARIABLE), + Value(env, AND, "and"), 5); + env->func("or", Proto::of(ARG_VARIABLE, "or", ARG_VARIABLE), + Value(env, OR, "or"), 5); + env->func("xor", Proto::of(ARG_VARIABLE, "xor", ARG_VARIABLE), + Value(env, XOR, "xor"), 5); + env->func("not", Proto::of("not", ARG_VARIABLE), + Value(env, NOT, "not"), 0); + env->func("==", Proto::of(ARG_VARIABLE, "==", ARG_VARIABLE), + Value(env, EQUALS, "=="), 10); + env->func("!=", Proto::of(ARG_VARIABLE, "!=", ARG_VARIABLE), + Value(env, NOT_EQUALS, "!="), 10); + env->func("<", Proto::of(ARG_VARIABLE, "<", ARG_VARIABLE), + Value(env, LESS, "<"), 10); + env->func(">", Proto::of(ARG_VARIABLE, ">", ARG_VARIABLE), + Value(env, GREATER, ">"), 10); + env->func("<=", Proto::of(ARG_VARIABLE, "<=", ARG_VARIABLE), + Value(env, LESS_EQUAL, "<="), 10); + env->func(">=", Proto::of(ARG_VARIABLE, ">=", ARG_VARIABLE), + Value(env, GREATER_EQUAL, ">="), 10); + env->func("in", Proto::of(ARG_VARIABLE, "in", ARG_VARIABLE), + Value(env, DICT_IN, "in"), 60); // env->infix("not", Value(env, DICT_NOT_IN), 2, 60); - env->def("display", Value(env, DISPLAY), 1); - env->infix("at", cases(env, &AT_INT, &AT_LIST, &AT_ARRAY_TYPE, &AT_DYNARRAY_TYPE, - &AT_MODULE, &AT_DICT, &AT_DICT_TYPE, &AT_DICT_LIST), 2, 120); - env->def("annotate", Value(env, ANNOTATE), 2); - env->def("typeof", Value(env, TYPEOF), 1); - env->infix_macro("of", Value(env, OF_TYPE_MACRO), 2, 20); - env->def("#of", Value(env, OF_TYPE), 2); - env->infix("list", Value(env, LIST_TYPE), 1, 80); - env->infix("#?", Value(env, IF), 3, 2); + env->func("display", Proto::of("display", ARG_VARIABLE), + Value(env, DISPLAY, "display"), 0); + env->func("at", Proto::of(ARG_VARIABLE, "at", ARG_VARIABLE), + cases(env, "at", &AT_INT, &AT_LIST, &AT_ARRAY_TYPE, &AT_DYNARRAY_TYPE, + &AT_MODULE, &AT_DICT, &AT_DICT_TYPE, &AT_DICT_LIST), 120); + env->func("annotate", Proto::of("annotate", ARG_VARIABLE, ARG_VARIABLE), + Value(env, ANNOTATE, "annotate"), 0); + env->func("typeof", Proto::of("typeof", ARG_VARIABLE), + Value(env, TYPEOF, "typeof"), 0); + env->macro("of", Proto::of(ARG_VARIABLE, "of", ARG_VARIABLE), + Value(env, OF_TYPE_MACRO, "of"), 20); + env->func("#of", Proto::of("#of", ARG_VARIABLE, ARG_VARIABLE), + Value(env, OF_TYPE, "of"), 0); + env->func("list", Proto::of(ARG_VARIABLE, "list"), + Value(env, LIST_TYPE, "list"), 80); + env->func("#?", Proto::of(ARG_VARIABLE, "#?", ARG_VARIABLE, ARG_VARIABLE), + Value(env, IF, "if"), 2); + env->func("eval", Proto::of("eval", ARG_VARIABLE), + Value(env, EVAL, "eval"), 0); + env->func("$flag", Proto::of("$flag", ARG_VARIABLE), + Value(env, FLAG, "flag"), 0); } } // namespace basil \ No newline at end of file diff --git a/compiler/builtin.h b/compiler/builtin.h index 02031ac..ba91c86 100644 --- a/compiler/builtin.h +++ b/compiler/builtin.h @@ -45,7 +45,8 @@ namespace basil { DISPLAY, READ_LINE, READ_WORD, READ_INT, LENGTH, AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, AT_DICT_TYPE, AT_MODULE, AT_DICT, AT_DICT_LIST, STRCAT, SUBSTR, ANNOTATE, TYPEOF, LIST_TYPE, OF_TYPE_MACRO, OF_TYPE, - ASSIGN, IF; + ASSIGN, IF, + EVAL, FLAG; } #endif \ No newline at end of file diff --git a/compiler/driver.cpp b/compiler/driver.cpp index fffded1..6262e99 100644 --- a/compiler/driver.cpp +++ b/compiler/driver.cpp @@ -61,8 +61,7 @@ namespace basil { TokenView end = view; while (end) end.read(); while (view.peek()) { - Value line = parse_line(view, view.peek().column); - if (!line.is_void()) results.push(line); + parse_line(results, view, view.peek().column); } if (_print_tokens) { print(BOLDYELLOW); @@ -71,7 +70,7 @@ namespace basil { } if (_print_parsed) { print(BOLDGREEN); - for (const Value& v : results) println(v); + for (const Value& v : results) print(v, " "); println(RESET); } return cons(string("do"), list_of(results)); diff --git a/compiler/env.cpp b/compiler/env.cpp index 388044c..85181fa 100644 --- a/compiler/env.cpp +++ b/compiler/env.cpp @@ -1,83 +1,123 @@ #include "env.h" namespace basil { - Def::Def(bool is_macro_in, bool is_procedure_in, bool is_infix_in, - u8 arity_in, u8 precedence_in): - Def(Value(), is_macro_in, is_procedure_in, is_infix_in, - arity_in, precedence_in) {} - Def::Def(Value value_in, bool is_macro_in, bool is_procedure_in, - bool is_infix_in, u8 arity_in, u8 precedence_in): - value(value_in), is_macro(is_macro_in), - is_proc(is_procedure_in), is_infix(is_infix_in), - arity(arity_in), precedence(precedence_in) {} + // Arg - bool Def::is_procedure() const { - return is_proc && !is_macro; + bool Arg::matches(const Value& term) const { + if (type == ARG_KEYWORD) return term.is_symbol() && term.get_symbol() == name; + else return true; } - bool Def::is_variable() const { - return !is_proc && !is_macro; + // Proto + + const Proto Proto::VARIABLE; + + Proto::Proto() {} + + Proto::Proto(const vector& args) { + overloads.push(args); } - bool Def::is_macro_procedure() const { - return is_proc && is_macro; + void Proto::add_arg(vector& proto, const ArgType& arg) { + proto.push({ arg, 0 }); // name is irrelevant so we pick 0 } - - bool Def::is_macro_variable() const { - return !is_proc && is_macro; + + void Proto::add_arg(vector& proto, const string& arg) { + proto.push({ ARG_VARIABLE, symbol_value(arg) }); } - Env::Env(const ref& parent): - _parent(parent), _runtime(false) {} + void Proto::add_args(vector& proto) {} - void Env::def(const string& name) { - _defs[name] = Def(false, false, false); + bool args_equal(const vector& a, const vector& b) { + if (a.size() != b.size()) return false; + for (u32 i = 0; i < a.size(); i ++) { + if (a[i].type != b[i].type) return false; + if (a[i].name != b[i].name) return false; + } + return true; } - void Env::def_macro(const string& name) { - _defs[name] = Def(true, false, false); + void Proto::overload(const Proto& other) { + for (const vector& v : other.overloads) { + bool duplicate = false; + for (const vector& w : other.overloads) { + if (args_equal(v, w)) { + duplicate = true; + break; + } + } + if (!duplicate) overloads.push(v); + } + } + + // Def + + Def::Def(): + Def(Proto(vector()), Value(VOID), false, 0) {} + + Def::Def(const Proto& proto_in, bool is_macro_in, u8 precedence_in): + Def(proto_in, Value(VOID), is_macro_in, precedence_in) {} + + Def::Def(const Proto& proto_in, const Value& value_in, bool is_macro_in, u8 precedence_in): + proto(proto_in), value(value_in), is_macro(is_macro_in), precedence(precedence_in) {} + + bool Def::is_procedure() const { + return !is_macro && proto.overloads.size() > 0; } - void Env::def(const string& name, u8 arity) { - _defs[name] = Def(false, true, false, arity); + bool Def::is_variable() const { + return !is_macro && proto.overloads.size() == 0; } - void Env::def_macro(const string& name, u8 arity) { - _defs[name] = Def(true, true, false, arity); + bool Def::is_macro_procedure() const { + return is_macro && proto.overloads.size() > 0; + } + + bool Def::is_macro_variable() const { + return is_macro && proto.overloads.size() == 0; + } + + bool Def::is_infix() const { + if (proto.overloads.size() == 0) return false; + for (const vector& v : proto.overloads) + if (v.size() == 0 || v[0].type == ARG_KEYWORD) return false; + return true; } - void Env::def(const string& name, const Value& value) { - _defs[name] = Def(value, false, false, false); + Env::Env(const ref& parent): + _parent(parent), _runtime(false) {} + + void Env::var(const string& name) { + _defs[name] = Def(Proto::VARIABLE, false, 0); } - void Env::def(const string& name, const Value& value, u8 arity) { - _defs[name] = Def(value, false, true, false, arity); + void Env::func(const string& name, const Proto& proto, u8 precedence) { + _defs[name] = Def(proto, false, precedence); } - void Env::def_macro(const string& name, const Value& value) { - _defs[name] = Def(value, true, false, false); + void Env::var(const string& name, const Value& value) { + _defs[name] = Def(Proto::VARIABLE, value, false, 0); } - void Env::def_macro(const string& name, const Value& value, u8 arity) { - _defs[name] = Def(value, true, true, false, arity); + void Env::func(const string& name, const Proto& proto, const Value& value, u8 precedence) { + _defs[name] = Def(proto, value, false, precedence); } - void Env::infix(const string& name, u8 arity, u8 precedence) { - _defs[name] = Def(false, true, true, arity, precedence); + void Env::alias(const string& name) { + _defs[name] = Def(Proto::VARIABLE, true, 0); } - void Env::infix(const string& name, const Value& value, u8 arity, u8 precedence) { - _defs[name] = Def(value, false, true, true, arity, precedence); + void Env::macro(const string& name, const Proto& proto, u8 precedence) { + _defs[name] = Def(proto, true, precedence); } - void Env::infix_macro(const string& name, u8 arity, u8 precedence) { - _defs[name] = Def(true, true, true, arity, precedence); + void Env::alias(const string& name, const Value& value) { + _defs[name] = Def(Proto::VARIABLE, value, true, 0); } - void Env::infix_macro(const string& name, const Value& value, - u8 arity, u8 precedence) { - _defs[name] = Def(value, true, true, true, arity, precedence); + void Env::macro(const string& name, const Proto& proto, const Value& value, u8 precedence) { + _defs[name] = Def(proto, value, true, precedence); } const Def* Env::find(const string& name) const { diff --git a/compiler/env.h b/compiler/env.h index 9c6b405..3e8f6fd 100644 --- a/compiler/env.h +++ b/compiler/env.h @@ -5,30 +5,76 @@ #include "util/hash.h" #include "util/str.h" #include "util/rc.h" +#include "util/vec.h" #include "values.h" #include "ssa.h" #include "ir.h" namespace basil { + enum ArgType { + ARG_VARIABLE, + ARG_KEYWORD, + ARG_VARIADIC + }; + + struct Arg { + ArgType type; + u64 name; + + bool matches(const Value& term) const; + }; + + class Proto { + static void add_arg(vector& proto, const ArgType& arg); + static void add_arg(vector& proto, const string& arg); + static void add_args(vector& proto); + + template + static void add_args(vector& proto, const T& arg, const Args&... rest) { + Proto::add_arg(proto, arg); + Proto::add_args(proto, rest...); + } + Proto(); + public: + const static Proto VARIABLE; + vector> overloads; + + Proto(const vector& args); + + template + static Proto of(const Args&... args) { + vector proto; + add_args(proto, args...); + return Proto(proto); + } + + template + static Proto overloaded(const Args&... args) { + vector protos = vector_of(args...); + Proto proto; + for (const Proto& other : protos) proto.overload(other); + return proto; + } + + void overload(const Proto& other); + }; + struct Def { Value value; + Proto proto; bool is_macro; // is the definition a macro alias or procedure? - bool is_infix; // is the definition for an infix procedure? - bool is_proc; // is the definition a scalar or procedure? - u8 arity; // number of arguments taken by a procedure. u8 precedence; // precedence of infix procedure SSAIdent ident; Location location; - Def(bool is_macro_in = false, bool is_procedure_in = false, - bool is_infix_in = false, u8 arity_in = 0, u8 precedence_in = 0); - Def(Value value_in, bool is_procedure_in = false, - bool is_macro_in = false, bool is_infix_in = false, - u8 arity_in = 0, u8 precedence_in = 0); + Def(); + Def(const Proto& proto_in, bool is_macro_in, u8 precedence_in); + Def(const Proto& proto_in, const Value& value_in, bool is_macro_in, u8 precedence_in); bool is_procedure() const; bool is_variable() const; bool is_macro_procedure() const; bool is_macro_variable() const; + bool is_infix() const; }; class Env { @@ -38,18 +84,14 @@ namespace basil { public: Env(const ref& parent = nullptr); - void def(const string& name); - void def(const string& name, u8 arity); - void def_macro(const string& name); - void def_macro(const string& name, u8 arity); - void def(const string& name, const Value& value); - void def(const string& name, const Value& value, u8 arity); - void def_macro(const string& name, const Value& value); - void def_macro(const string& name, const Value& value, u8 arity); - void infix(const string& name, u8 arity, u8 precedence); - void infix(const string& name, const Value& value, u8 arity, u8 precedence); - void infix_macro(const string& name, u8 arity, u8 precedence); - void infix_macro(const string& name, const Value& value, u8 arity, u8 precedence); + void var(const string& name); + void func(const string& name, const Proto& proto, u8 precedence); + void var(const string& name, const Value& value); + void func(const string& name, const Proto& proto, const Value& value, u8 precedence); + void alias(const string& name); + void macro(const string& name, const Proto& proto, u8 precedence); + void alias(const string& name, const Value& value); + void macro(const string& name, const Proto& proto, const Value& value, u8 precedence); const Def* find(const string& name) const; Def* find(const string& name); void format(stream& io) const; diff --git a/compiler/errors.cpp b/compiler/errors.cpp index 9b13d8a..52b19d7 100644 --- a/compiler/errors.cpp +++ b/compiler/errors.cpp @@ -1,18 +1,55 @@ #include "errors.h" +#include "values.h" #include "util/vec.h" #include "lex.h" namespace basil { SourceLocation::SourceLocation(): - line(0), column(0), length(0) {} + line_start(0), line_end(0), column_start(0), column_end(0) {} - SourceLocation::SourceLocation(u32 line_in, u16 column_in, u16 length_in): - line(line_in), column(column_in), length(length_in) {} + SourceLocation::SourceLocation(u32 line, u16 col): + line_start(line), column_start(col), line_end(line), column_end(col + 1) {} + + SourceLocation::SourceLocation(u32 line_start_in, u16 column_start_in, u32 line_end_in, u16 column_end_in): + line_start(line_start_in), column_start(column_start_in), line_end(line_end_in), column_end(column_end_in) {} SourceLocation::SourceLocation(const Token& token): - line(token.line), column(token.column), length(token.value.size()) {} + line_start(token.line), column_start(token.column), line_end(token.line), column_end(token.column + token.value.size()) {} + + bool SourceLocation::empty() const { + return line_start == line_end && column_start == column_end; + } + + SourceLocation span(SourceLocation a, SourceLocation b) { + if (a.line_start * 65536 + a.column_start < b.line_start * 65536 + b.column_start) + return { a.line_start, a.column_start, b.line_end, b.column_end }; + else + return { b.line_start, b.column_start, a.line_end, a.column_end }; + } - const SourceLocation NO_LOCATION = { 0, 0, 0 }; + const SourceLocation NO_LOCATION; + + string commalist(const Value& value, bool quote) { + buffer b; + vector values; + if (value.is_list()) values = to_vector(value); + else if (value.is_array()) values = value.get_array().values(); + else if (value.is_product()) values = value.get_product().values(); + else if (value.is_type() && value.get_type()->kind() == KIND_PRODUCT) { + const ProductType* p = (const ProductType*)value.get_type(); + for (u32 i = 0; i < p->count(); i ++) values.push(Value(p->member(i), TYPE)); + } + for (u32 i = 0; i < values.size(); i ++) { + if (i > 0) { + if (i == values.size() - 1 && i > 0) write(b, " and "); + else write(b, ", "); + } + write(b, quote ? "'" : "", values[i], quote ? "'" : ""); + } + string s; + while (b) s += b.read(); + return s; + } struct Error { SourceLocation loc; @@ -36,8 +73,8 @@ namespace basil { void print_errors(stream& io) { for (const Error& e : errors) { - if (e.loc.length != 0) - write(io, "[", e.loc.line + 1, ":", e.loc.column + 1, "] "); + if (!e.loc.empty()) + write(io, "[", e.loc.line_start + 1, ":", e.loc.column_start + 1, "] "); writeln(io, e.message); } clear_errors(); @@ -45,15 +82,15 @@ namespace basil { void print_errors(stream& io, const Source& src) { for (const Error& e : errors) { - if (e.loc.length != 0) - write(io, "[", e.loc.line + 1, ":", e.loc.column + 1, "] "); + if (!e.loc.empty()) + write(io, "[", e.loc.line_start + 1, ":", e.loc.column_start + 1, "] "); writeln(io, e.message); - if (e.loc.length == 0) continue; // skip source printing if no location - const auto& line = src.line(e.loc.line); + if (e.loc.empty()) continue; // skip source printing if no location + const auto& line = src.line(e.loc.line_start); if (line.back() == '\n') write(io, '\t', line); else writeln(io, '\t', line); - u32 first = e.loc.column, last = e.loc.column + e.loc.length; + u32 first = e.loc.column_start, last = e.loc.line_end == e.loc.line_start ? e.loc.column_end : line.size(); u32 i = 0; write(io, '\t'); for (; i < first; i ++) write(io, ' '); diff --git a/compiler/errors.h b/compiler/errors.h index c42e557..8c240dc 100644 --- a/compiler/errors.h +++ b/compiler/errors.h @@ -9,17 +9,23 @@ namespace basil { class Token; class Source; + class Value; struct SourceLocation { - u32 line; - u16 column, length; + u32 line_start, line_end; + u16 column_start, column_end; SourceLocation(); - SourceLocation(u32 line_in, u16 column_in, u16 length_in = 1); + SourceLocation(u32 line, u16 col); + SourceLocation(u32 line_start_in, u16 column_start_in, u32 line_end_in, u16 column_end_in); SourceLocation(const Token& token); + // expand later with other types of objects + bool empty() const; }; + SourceLocation span(SourceLocation a, SourceLocation b); + // has length of zero, indicates position not in source extern const SourceLocation NO_LOCATION; @@ -30,6 +36,8 @@ namespace basil { void print_errors(stream& io, const Source& src); void silence_errors(); void unsilence_errors(); + + string commalist(const Value& value, bool quote); template void err(SourceLocation loc, Args... args) { diff --git a/compiler/eval.cpp b/compiler/eval.cpp index 0c64e9a..2e9c619 100644 --- a/compiler/eval.cpp +++ b/compiler/eval.cpp @@ -163,7 +163,7 @@ namespace basil { ref create_root_env() { ref root = newref(); - root->def("nil", Value(VOID)); + root->var("nil", Value(VOID)); // root->infix("+", new FunctionValue(root, builtin_add, 2), 2, 20); // root->infix("-", new FunctionValue(root, builtin_sub, 2), 2, 20); // root->infix("*", new FunctionValue(root, builtin_mul, 2), 2, 40); @@ -200,14 +200,24 @@ namespace basil { // root->def("typeof", new FunctionValue(root, builtin_typeof, 1), 1); define_builtins(root); - root->def("true", Value(true, BOOL)); - root->def("false", Value(false, BOOL)); - root->def("int", Value(INT, TYPE)); - root->def("symbol", Value(SYMBOL, TYPE)); - root->def("string", Value(STRING, TYPE)); - root->def("type", Value(TYPE, TYPE)); - root->def("bool", Value(BOOL, TYPE)); - root->def("void", Value(VOID, TYPE)); + root->var("true", Value(true, BOOL)); + root->var("false", Value(false, BOOL)); + root->var("int", Value(INT, TYPE)); + root->var("symbol", Value(SYMBOL, TYPE)); + root->var("string", Value(STRING, TYPE)); + root->var("type", Value(TYPE, TYPE)); + root->var("bool", Value(BOOL, TYPE)); + root->var("void", Value(VOID, TYPE)); + + root->func("def", Proto::of(ARG_VARIABLE, ARG_VARIABLE), 0); + root->func("macro", Proto::of(ARG_VARIABLE, ARG_VARIABLE), 0); + root->func("lambda", Proto::of(ARG_VARIABLE, ARG_VARIABLE), 0); + root->func("quote", Proto::of(ARG_VARIABLE), 0); + root->func("splice", Proto::of(ARG_VARIABLE), 0); + root->func("use", Proto::overloaded( + Proto::of(ARG_VARIABLE), + Proto::of(ARG_VARIABLE, "as", ARG_VARIABLE) + ), 0); return root; } @@ -360,8 +370,8 @@ namespace basil { } bool is_valid_argument(const Value& term) { - return term.is_symbol() && symbol_for(term.get_symbol()).endswith('?') // name - || is_annotation(term) && tail(term).is_list() && head(tail(term)).is_symbol(); + return term.is_symbol() && symbol_for(term.get_symbol()).endswith('?'); // name + // || is_annotation(term) && tail(term).is_list() && head(tail(term)).is_symbol(); } bool is_valid_def(const Value& term) { @@ -380,17 +390,17 @@ namespace basil { } string get_arg_name(const Value& term) { - if (is_annotation(term)) return get_arg_name(head(tail(term))); - else { + // if (is_annotation(term)) return get_arg_name(head(tail(term))); + // else { const string& name = symbol_for(term.get_symbol()); return name[{0, name.size() - 1}]; - } + // } } string get_def_name(const Value& term) { if (term.is_symbol()) return symbol_for(term.get_symbol()); - else if (is_annotation(term)) - return get_def_name(head(tail(term))); + // else if (is_annotation(term)) + // return get_def_name(head(tail(term))); else return symbol_for(head(term).get_symbol()); } @@ -401,7 +411,7 @@ namespace basil { } string get_infix_name(const Value& term) { - if (is_annotation(term)) return get_infix_name(head(tail(term))); + // if (is_annotation(term)) return get_infix_name(head(tail(term))); return symbol_for(head(tail(term)).get_symbol()); } @@ -421,6 +431,23 @@ namespace basil { return false; } + Proto proto_from_list(const Value& term) { + vector terms = to_vector(term); + vector args; + for (const Value& v : terms) { + if (v.is_symbol()) { + const string& name = symbol_for(v.get_symbol()); + if (name.endswith('?')) args.push({ ARG_VARIABLE, 0 }); + else args.push({ ARG_KEYWORD, v.get_symbol() }); + } + else { + err(v.loc(), "Unexpected term in parameter list: expected symbol."); + break; + } + } + return Proto(args); + } + void visit_defs(ref env, const Value& item) { if (!item.is_list()) return; Value h = head(item); @@ -444,42 +471,45 @@ namespace basil { // err(values[i].loc(), "Redefinition of '", name, "'."); // return; // } - if (!env->find(name)) env->def(name); - } else if (is_valid_def(values[i])) { // procedure - const string& name = get_def_name(values[i]); + if (!env->find(name)) env->var(name); + } else if (is_valid_def(values[i]) || is_valid_infix_def(values[i])) { // procedure + string name; + + // get correct name + if (is_valid_def(values[i])) { + name = get_def_name(values[i]); + } + else { + Value rest = tail(values[i]); + if (!rest.is_list()) { + err(rest.loc(), "Infix procedure must take at least one ", "argument."); + return; + } + name = get_infix_name(values[i]); + } + + // check def name and arg count if (name.endswith('?')) { err(values[i].loc(), "Invalid name for procedure: cannot end in '?'."); return; } - if (has_precedence) { - err(values[i - 1].loc(), "Precedence cannot be specified for non-infix procedures."); - return; - } if (values.size() < 3) { - err(item.loc(), "Expected procedure body in procedure definition."); + err(item.loc(), "Expected procedure body in infix procedure definition."); return; } + + // build prototype + Proto proto = proto_from_list(values[i]); + // if (env->find(name)) { // err(values[i].loc(), "Redefinition of '", name, "'."); // return; // } - if (!env->find(name)) env->def(name, to_vector(tail(values[i])).size()); - } else if (is_valid_infix_def(values[i])) { // infix procedure - Value rest = tail(values[i]); - if (!rest.is_list()) { - err(rest.loc(), "Infix procedure must take at least one ", "argument."); - return; - } - string name = get_infix_name(values[i]); - if (name.endswith('?')) { - err(values[i].loc(), "Invalid name for infix procedure: cannot end in '?'."); - return; - } - if (values.size() < 3) { - err(item.loc(), "Expected procedure body in infix procedure definition."); - return; + Def* existing = env->find(name); + if (!existing) env->func(name, proto, precedence); + else { + existing->proto.overload(proto); } - if (!env->find(name)) env->infix(name, to_vector(tail(rest)).size() + 1, precedence); } else { err(item.loc(), "Invalid definition."); return; @@ -563,17 +593,23 @@ namespace basil { } Value apply_op(const Value& op, const Value& lhs) { - return list_of(op, lhs); + Value v = list_of(op, lhs); + v.set_location(span(op.loc(), lhs.loc())); + return v; } Value apply_op(const Value& op, const Value& lhs, const Value& rhs) { - return list_of(op, lhs, rhs); + Value v = list_of(op, lhs, rhs); + v.set_location(span(lhs.loc(), rhs.loc())); + return v; } Value apply_op(const Value& op, const Value& lhs, const vector& internals, const Value& rhs) { Value l = list_of(rhs); for (i64 i = i64(internals.size()) - 1; i >= 0; i--) { l = cons(internals[i], l); } - return cons(op, cons(lhs, l)); + Value v = cons(op, cons(lhs, l)); + v.set_location(span(lhs.loc(), rhs.loc())); + return v; } struct InfixResult { @@ -581,115 +617,201 @@ namespace basil { bool changed; }; - InfixResult unary_helper(ref env, const Value& lhs, const Value& term); + InfixResult unary_helper(ref env, const Value& lhs, const Value& term, u8 minprecedence); InfixResult infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& rhs, - const Value& term, const vector& internals); - InfixResult infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& term); - InfixResult infix_transform(ref env, const Value& term); + const Value& term, const vector& internals, u8 minprecedence); + InfixResult infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& term, + u8 minprecedence); + InfixResult infix_transform(ref env, const Value& term, u8 minprecedence); + InfixResult prefix_helper(ref env, const Value& term); InfixResult infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& rhs, - const Value& term, const vector& internals) { + const Value& term, const vector& internals, u8 minprecedence = 0) { Value iter = term; if (iter.is_void()) return {apply_op(op, lhs, internals, rhs), iter, true}; Value next_op = head(iter); if (!next_op.is_symbol()) return {apply_op(op, lhs, internals, rhs), iter, true}; const Def* next_def = env->find(symbol_for(next_op.get_symbol())); - if (!next_def || !next_def->is_infix) return {apply_op(op, lhs, internals, rhs), iter, true}; + if (!next_def || !next_def->is_infix() || (next_def->is_procedure() && next_def->precedence < minprecedence)) + return {apply_op(op, lhs, internals, rhs), iter, true}; iter = tail(iter); // consume op if (next_def->precedence > def->precedence) { - if (next_def->arity == 1) { - return infix_helper(env, lhs, op, def, apply_op(next_op, rhs), iter, internals); - } - auto p = infix_helper(env, rhs, next_op, next_def, iter); + // if (next_def->arity == 1) { + // return infix_helper(env, lhs, op, def, apply_op(next_op, rhs), iter, internals, minprecedence); + // } + auto p = infix_helper(env, rhs, next_op, next_def, iter, minprecedence); return {apply_op(op, lhs, internals, p.first), p.second, true}; } else { Value result = apply_op(op, lhs, rhs); - if (next_def->arity == 1) { return unary_helper(env, apply_op(next_op, result), iter); } - return infix_helper(env, apply_op(op, lhs, internals, rhs), next_op, next_def, iter); + // if (next_def->arity == 1) { return unary_helper(env, apply_op(next_op, result), iter, minprecedence); } + return infix_helper(env, apply_op(op, lhs, internals, rhs), next_op, next_def, iter, minprecedence); } } - InfixResult infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& term) { + InfixResult infix_helper(ref env, const Value& lhs, const Value& op, const Def* def, const Value& term, u8 minprecedence = 0) { Value iter = term; vector internals; - if (def->arity > 2) - for (u32 i = 0; i < def->arity - 2; i++) { - auto p = infix_transform(env, iter); - internals.push(p.first); - iter = p.second; - } + // if (def->arity > 2) { + // for (u32 i = 0; i < def->arity - 2; i++) { + // auto p = infix_transform(env, iter, minprecedence); + // internals.push(p.first); + // iter = p.second; + // } + // } if (iter.is_void()) { - err(term.loc(), "Expected term in binary expression."); + err(op.loc(), "Missing rightmost term in infix expression."); return {error(), term, false}; } - Value rhs = head(iter); - iter = tail(iter); // consume second term + InfixResult prefix = prefix_helper(env, iter); + Value rhs = prefix.first; + iter = prefix.second; - return infix_helper(env, lhs, op, def, rhs, iter, internals); + return infix_helper(env, lhs, op, def, rhs, iter, internals, minprecedence); } - InfixResult unary_helper(ref env, const Value& lhs, const Value& term) { + InfixResult unary_helper(ref env, const Value& lhs, const Value& term, u8 minprecedence = 0) { Value iter = term; if (iter.is_void()) return {lhs, iter, false}; Value op = head(iter); if (!op.is_symbol()) return {lhs, iter, false}; const Def* def = env->find(symbol_for(op.get_symbol())); - if (!def || !def->is_infix) return {lhs, iter, false}; + if (!def || !def->is_infix() || (def->is_procedure() && def->precedence < minprecedence)) return {lhs, iter, false}; iter = tail(iter); // consume op - if (def->arity == 1) return unary_helper(env, apply_op(op, lhs), iter); - return infix_helper(env, lhs, op, def, iter); + // if (def->arity == 1) return unary_helper(env, apply_op(op, lhs), iter, minprecedence); + return infix_helper(env, lhs, op, def, iter, minprecedence); + } + + InfixResult prefix_helper(ref env, const Value& term) { + Value iter = term; + Value lhs = head(iter); + iter = tail(iter); + if (lhs.is_symbol()) { + Def* def = env->find(symbol_for(lhs.get_symbol())); + if (def && def->is_procedure()) { + u8 fprecedence = def->precedence; + vector args; + bool changed = false; + const Proto& proto = def->proto; + map indices; + i32 best = -1; // -1 represents no match + Value saved_iter = iter; + bool foundbest = false; + for (u32 i = 0; i < proto.overloads.size(); i ++) { + indices[i] = 1; + if (proto.overloads[i].size() == 0) { + if (foundbest) err(lhs.loc(), "Ambiguous call to function '", + symbol_for(lhs.get_symbol()), "', multiple patterns match ", + "available arguments."); + best = i; + foundbest = true; + } + } + vector toRemove; + while (!iter.is_void()) { + foundbest = false; + Value term = head(iter); + for (auto& p : indices) { + if (p.second < proto.overloads[p.first].size()) { + const Arg& arg = proto.overloads[p.first][p.second]; + if (!arg.matches(term)) toRemove.push(p.first); + else { + p.second ++; + if (p.second == proto.overloads[p.first].size()) { + if (foundbest) err(lhs.loc(), "Ambiguous call to function '", + symbol_for(lhs.get_symbol()), "', multiple patterns match ", + "available arguments."); + best = p.first; + saved_iter = tail(iter); + foundbest = true; + } + } + } + } + for (u32 i : toRemove) indices.erase(i); + if (indices.size() == 0) break; + InfixResult ir = infix_transform(env, iter, fprecedence); + args.push(ir.first); + iter = ir.second; + changed = changed || ir.changed; + if (iter.is_void()) break; + Value op = head(iter); + if (op.is_symbol()) { + Def* def = env->find(symbol_for(op.get_symbol())); + if (def && def->is_infix() && def->precedence < fprecedence) break; + } + } + if (args.size() == 0 || best == -1) return { lhs, iter, false }; + else { + vector subargs; + for (u32 i = 0; i < proto.overloads[best].size(); i ++) + subargs.push(args[i]); + return { cons(lhs, list_of(subargs)), saved_iter, true }; + } + } + } + return { lhs, iter, false }; } - InfixResult infix_transform(ref env, const Value& term) { + InfixResult infix_transform(ref env, const Value& term, u8 minprecedence) { Value iter = term; if (iter.is_void()) { err(term.loc(), "Expected term in binary expression."); return {error(), term}; } - Value lhs = head(iter); // 1 + 2 -> 1 - iter = tail(iter); // consume first term + InfixResult prefix = prefix_helper(env, iter); + Value lhs = prefix.first; + iter = prefix.second; - if (iter.is_void()) return {lhs, iter, false}; + if (iter.is_void()) return {lhs, iter, prefix.changed}; Value op = head(iter); - if (!op.is_symbol()) return {lhs, iter, false}; + if (!op.is_symbol()) return {lhs, iter, prefix.changed}; const Def* def = env->find(symbol_for(op.get_symbol())); - if (!def || !def->is_infix) return {lhs, iter, false}; + if (!def || !def->is_infix()) return {lhs, iter, prefix.changed}; iter = tail(iter); // consume op - if (def->arity == 1) return unary_helper(env, apply_op(op, lhs), iter); - return infix_helper(env, lhs, op, def, iter); + // if (def->arity == 1) return unary_helper(env, apply_op(op, lhs), iter, minprecedence); + return infix_helper(env, lhs, op, def, iter, minprecedence); } - Value handle_infix(ref env, const Value& term) { + pair handle_infix(ref env, const Value& term) { vector infix_exprs; Value iter = term; bool changed = false; while (iter.is_list()) { - auto p = infix_transform(env, iter); + auto p = infix_transform(env, iter, 0); infix_exprs.push(p.first); // next s-expr iter = p.second; // move past it in source list changed = changed || p.changed; } - Value result = infix_exprs.size() == 1 && changed ? infix_exprs[0] : list_of(infix_exprs); - return result; + Value result = list_of(infix_exprs); + result.set_location(term.loc()); + return { result, changed }; } void apply_infix(ref env, Value& item); void apply_infix_at(ref env, Value& item, u32 depth) { Value* iter = &item; + u32 initial_depth = depth; while (depth && iter->is_list()) { iter = &iter->get_list().tail(); depth--; } - *iter = handle_infix(env, *iter); + Value t = item.clone(); + pair v = handle_infix(env, *iter); + if (initial_depth == 0 && length(v.first) == 1 && v.second) { + Value h = head(v.first); + *iter = h; + } + else + *iter = v.first; traverse_list(env, *iter, apply_infix); } @@ -698,19 +820,18 @@ namespace basil { Value h = head(item); if (h.is_symbol()) { const string& name = symbol_for(h.get_symbol()); - if (name == "macro" || name == "lambda" || name == "splice") return; - // else if (name == "def") { - // if (tail(item).is_list() && head(tail(item)).is_symbol()) apply_infix_at(env, item, 2); - // } + if (name == "macro" || name == "splice") return; + else if (name == "def" || name == "lambda") { + if (tail(item).is_list() && head(tail(item)).is_symbol()) apply_infix_at(env, item, 2); + else apply_infix_at(env, item, 2); + } else if (name == "if" || name == "do" || name == "list-of") apply_infix_at(env, item, 1); else { - silence_errors(); - Value proc = eval(env, h); - unsilence_errors(); - if (proc.is_function()) apply_infix_at(env, item, 1); - else - apply_infix_at(env, item, 0); + // Def* def = env->find(name); + // if (def && def->arity > 0) apply_infix_at(env, item, 1); + // else + apply_infix_at(env, item, 0); } } else apply_infix_at(env, item, 0); @@ -720,15 +841,15 @@ namespace basil { handle_splice(env, term); handle_use(env, term); visit_macro_defs(env, term); - visit_defs(env, term); apply_infix(env, term); + handle_splice(env, term); + handle_use(env, term); handle_macro(env, term); visit_defs(env, term); apply_infix(env, term); - // define_procedures(env, term); } - Value overload(ref env, Def* existing, const Value& func, const string& name) { + Value overload(ref env, Def* existing, const Proto& proto, const Value& func, const string& name) { const Type* argst = (const ProductType*)((const FunctionType*)func.type())->arg(); if (existing->value.is_function()) { // create new intersect const Type* existing_args = ((const FunctionType*)existing->value.type())->arg(); @@ -743,6 +864,7 @@ namespace basil { values.put(func.type(), func); existing->value = Value(new IntersectValue(values), find(existing->value.type(), func.type())); + existing->value.set_name(name); return Value(VOID); } else if (existing->value.is_intersect()) { // add to existing intersect for (const auto& p : existing->value.get_intersect()) { @@ -758,8 +880,10 @@ namespace basil { } map values = existing->value.get_intersect().values(); values.put(func.type(), func); + existing->proto.overload(proto); existing->value = Value(new IntersectValue(values), find(existing->value.type(), func.type())); + existing->value.set_name(name); return Value(VOID); } else { err(func.loc(), "Cannot redefine symbol '", name, "' of type '", existing->value.type(), "' as function."); @@ -791,13 +915,13 @@ namespace basil { err(values[i].loc(), "Type annotations are forbidden in macro definitions."); return error(); } - env->def_macro(name, Value(new AliasValue(values[i + 1]))); + env->alias(name, Value(new AliasValue(values[i + 1]))); return Value(VOID); } else { Value init = eval(env, values[i + 1]); if (env->is_runtime()) init = lower(init); if (is_annotation(values[i])) init = annotate(init, eval(env, annotation_type(values[i]))); - env->def(name, init); + env->var(name, init); if (init.is_runtime()) return new ASTDefine(values[0].loc(), env, values[i].get_symbol(), init.get_runtime()); return Value(VOID); @@ -815,11 +939,12 @@ namespace basil { vector argnames; vector body; vector argts; + Proto proto = proto_from_list(values[i]); for (const Value& v : args) { if (is_valid_argument(v)) { string name = get_arg_name(v); argnames.push(symbol_value(name)); - function_env->def(name); + function_env->var(name); } else if (is_keyword(v)) { argnames.push(v.get_symbol() | KEYWORD_ARG_BIT); } else { @@ -856,9 +981,7 @@ namespace basil { return error(); } Value mac(new MacroValue(function_env, argnames, body_term)); - if (infix) env->infix_macro(name, mac, argnames.size(), precedence); - else - env->def_macro(name, mac, argnames.size()); + env->macro(name, proto, mac, precedence); } else { const Type* returntype = ANY; if (is_annotation(values[i])) { @@ -890,11 +1013,11 @@ namespace basil { : Value(new FunctionValue(function_env, argnames, body_term, fname), ft); Def* existing = env->find(name); if (existing && !existing->value.is_void()) { - return overload(env, existing, func, name); - } else if (infix) { - env->infix(name, func, argnames.size(), precedence); - } else - env->def(name, func, argnames.size()); + return overload(env, existing, proto, func, name); + } else { + func.set_name(name); + env->func(name, proto, func, precedence); + } if (func.is_runtime()) return new ASTDefine(values[0].loc(), env, fname, func.get_runtime()); if (argst->concrete() && !external) { func.get_function().instantiate(argst, new ASTIncompleteFn(func.loc(), argst, symbol_value(name))); @@ -909,111 +1032,6 @@ namespace basil { } } - Value infix(ref env, const Value& term, bool is_macro) { - vector values = to_vector(term); - - u8 precedence = 0; - - u32 i = 1; - if (values[i].is_int()) { // precedence - precedence = (u8)values[i].get_int(); - i++; - } - - Value def_info = get_def_info(values[i]); - bool infix = false; - if (!is_valid_def(def_info)) infix = false; - const string& name = symbol_for(head(tail(def_info)).get_symbol()); - - vector args; - args.push(head(def_info)); - Value v = tail(tail(def_info)); - while (v.is_list()) { - args.push(head(v)); - v = tail(v); - } - if (args.size() == 0) { - err(values[i].loc(), "Expected argument list in infix ", "definition."); - return error(); - } - i++; - ref function_env = newref(env); - vector argnames; - vector argts; - for (const Value& v : args) { - if (is_valid_argument(v)) { - string name = get_arg_name(v); - argnames.push(symbol_value(name)); - function_env->def(name); - } else if (is_keyword(v)) { - argnames.push(eval(env, v).get_symbol() | KEYWORD_ARG_BIT); - } else { - err(v.loc(), - "Only symbols, annotated symbols, and quoted symbols " - "are permitted within an argument list; given '", - v, "'."); - return error(); - } - if (is_annotation(v)) { - if (is_macro) { - err(v.loc(), "Type annotations are forbidden in macro definitions."); - return error(); - } - Value tval = eval(env, annotation_type(v)); - if (!tval.is_type()) { - if (tval.type()->coerces_to(TYPE)) tval = cast(tval, TYPE); - else { - err(v.loc(), "Expected type value in annotation, given '", tval.type(), "'."); - return error(); - } - } - // body.push(v); - // body.push(list_of(Value("list-of"))); - argts.push(tval.get_type()); - } else if (!is_keyword(v)) - argts.push(ANY); - } - vector body; - for (int j = i; j < values.size(); j++) body.push(values[j]); - Value body_term = cons(Value("do"), list_of(body)); - if (is_macro) { - if (is_annotation(values[i])) { - err(values[i].loc(), "Type annotations are forbidden in macro definitions."); - return error(); - } - Value mac(new MacroValue(function_env, argnames, body_term)); - env->infix_macro(name, mac, argnames.size(), precedence); - } else { - const Type* returntype = ANY; - if (is_annotation(values[i])) { - Value tval = eval(env, annotation_type(values[i])); - if (!tval.is_type()) { - if (tval.type()->coerces_to(TYPE)) tval = cast(tval, TYPE); - else { - err(values[i].loc(), "Expected type value in annotation, given '", tval.type(), "'."); - return error(); - } - } - returntype = tval.get_type(); - } - const Type* argst = find(argts); - Value func(new FunctionValue(function_env, argnames, body_term, symbol_value(name)), - find(argst, returntype)); - Def* existing = env->find(name); - if (existing && !existing->value.is_void()) { - if (existing->precedence != precedence) { - err(func.loc(), "Cannot overload infix function '", name, "' with function of incompatible ", - "precedence ", (u32)precedence, " (original was ", (u32)existing->precedence, ")."); - return error(); - } - return overload(env, existing, func, name); - } else - env->infix(name, func, argnames.size(), precedence); - // if (argst->concrete()) instantiate(func.loc(), func.get_function(), find(argts)); - } - return Value(VOID); - } - Value lambda(ref env, const Value& term) { vector values = to_vector(term); if (values.size() < 3) { @@ -1032,7 +1050,7 @@ namespace basil { if (is_valid_argument(v)) { string name = get_arg_name(v); argnames.push(symbol_value(name)); - function_env->def(name); + function_env->var(name); } else if (is_keyword(v)) { argnames.push(eval(env, v).get_symbol() | KEYWORD_ARG_BIT); } else { @@ -1269,6 +1287,16 @@ namespace basil { static map modules; + Value module_decl(ref env, const Value& term) { + Value decls = cons(Value("do", SYMBOL), tail(term)); + ref newenv = newref(env); + prep(newenv, decls); + eval(newenv, decls); + map values; + for (const auto& p : *newenv) values.put(symbol_value(p.first), p.second.value); + return Value(new ModuleValue(values)); + } + Value use(ref env, const Value& term) { if (!tail(term).is_list() || tail(term).is_void()) { err(tail(term).loc(), "Expected body in use expression."); @@ -1278,6 +1306,7 @@ namespace basil { string path, module_name; set names; Value h = head(tail(term)); + // use -> introduce a modulevalue with name into the env // use as -> introduce a modulevalue with name into the env if (head(tail(term)).is_symbol()) { @@ -1301,8 +1330,6 @@ namespace basil { return error(); } module_name = symbol_for(head(tail(as_exp)).get_symbol()); - } else { - module_name = path; } } else { // use [ ... ] -> introduce names from [ ... ] into the env if (!head(h).is_symbol() || symbol_for(head(h).get_symbol()) != "at") { @@ -1333,7 +1360,9 @@ namespace basil { path = symbol_for(head(tail(h)).get_symbol()); } - path += ".bl"; + if (string(path[{path.size() - 3, path.size()- 1}]) != ".bl") { + path += ".bl"; + } ref module; auto it = modules.find(path); @@ -1359,10 +1388,12 @@ namespace basil { env->import_single(p.first, p.second); } } - } else { // use | use as + } else if (module_name.size() > 0) { // use as map values; for (const auto& p : *module) values.put(symbol_value(p.first), p.second.value); - env->def(module_name, Value(new ModuleValue(values))); + env->var(module_name, Value(new ModuleValue(values))); + } else { + env->import(module); } modules.put(path, unit); @@ -1398,13 +1429,15 @@ namespace basil { return use(env, term); else if (name == "while") return while_stmt(env, term); + else if (name == "module") + return module_decl(env, term); } Value first = eval(env, h); if (h.is_symbol() && tail(term).is_void()) { const Def* def = env->find(symbol_for(h.get_symbol())); - if (def && def->is_infix) return first; + if (def && def->is_infix()) return first; } if (first.is_macro()) { @@ -1438,7 +1471,7 @@ namespace basil { v = &v->get_list().tail(); i++; } - return call(env, first, Value(new ProductValue(args))); + return call(env, first, Value(new ProductValue(args)), term.loc()); } else if (first.is_runtime() && first.get_runtime()->type()->kind() == KIND_FUNCTION) { vector args; Value args_term = tail(term); @@ -1456,7 +1489,7 @@ namespace basil { err(term.loc(), "Procedure expects ", args_type->count(), " arguments, ", args.size(), " provided."); return error(); } - return call(env, first, Value(new ProductValue(args))); + return call(env, first, Value(new ProductValue(args)), term.loc()); } if (tail(term).is_void()) return first; @@ -1474,10 +1507,16 @@ namespace basil { else if (term.is_symbol()) { const string& name = symbol_for(term.get_symbol()); const Def* def = env->find(name); - if (def && def->is_macro_variable()) return def->value.get_alias().value(); + if (def && def->is_macro_variable()) { + Value result = def->value.get_alias().value(); + result.set_location(term.loc()); + return result; + } else if (def) { if (def->value.is_runtime()) return new ASTVar(term.loc(), env, term.get_symbol()); - return def->value; + Value result = def->value; + result.set_location(term.loc()); + return result; } else { err(term.loc(), "Undefined variable '", name, "'."); return error(); diff --git a/compiler/main.cpp b/compiler/main.cpp index 1a210a5..4036b92 100644 --- a/compiler/main.cpp +++ b/compiler/main.cpp @@ -12,8 +12,8 @@ void print_banner() { println(""); println("┌────────────────────────────────────────┐"); println("│ │"); - println("│ ", BOLDGREEN, R"(𝐵𝑎𝑠𝑖𝑙)", - RESET, " — version 0.1 │"); + println("│ ", BOLDGREEN, R"(Basil)", + RESET, " — version 0.1 │"); println("│ │"); println("└────────────────────────────────────────┘"); println(RESET); @@ -84,13 +84,20 @@ int main(int argc, char** argv) { Source src; ref root = create_root_env(); - root->def("$help", Value(root, REPL_HELP), 0); - root->def("$quit", Value(root, REPL_QUIT), 0); - root->def("$print-tokens", Value(root, PRINT_TOKENS), 1); - root->def("$print-parse", Value(root, PRINT_PARSE), 1); - root->def("$print-ast", Value(root, PRINT_AST), 1); - root->def("$print-ir", Value(root, PRINT_IR), 1); - root->def("$print-asm", Value(root, PRINT_ASM), 1); + root->func("$quit", Proto::of("$quit"), + Value(root, REPL_QUIT), 0); + root->func("$help", Proto::of("$help"), + Value(root, REPL_HELP), 0); + root->func("$print-tokens", Proto::of("$print-tokens", ARG_VARIABLE), + Value(root, PRINT_TOKENS), 0); + root->func("$print-parse", Proto::of("$print-parse", ARG_VARIABLE), + Value(root, PRINT_PARSE), 0); + root->func("$print-ast", Proto::of("$print-ast", ARG_VARIABLE), + Value(root, PRINT_AST), 0); + root->func("$print-ir", Proto::of("$print-ir", ARG_VARIABLE), + Value(root, PRINT_IR), 0); + root->func("$print-asm", Proto::of("$print-asm", ARG_VARIABLE), + Value(root, PRINT_ASM), 0); ref global = newref(root); Function main_fn("main"); @@ -835,11 +842,16 @@ int intro() { INTRO_SET_SECTION(find(find(INT), VOID), intro_set_section, nullptr); ref root = create_root_env(); - root->def("$quit", Value(root, REPL_QUIT), 0); - root->def("$help", Value(root, INTRO_HELP), 0); - root->def("$contents", Value(root, INTRO_CONTENTS), 0); - root->def("$start", Value(root, INTRO_START), 0); - root->def("$section", Value(root, INTRO_SET_SECTION), 1); + root->func("$quit", Proto::of("$quit"), + Value(root, REPL_QUIT), 0); + root->func("$help", Proto::of("$help"), + Value(root, INTRO_HELP), 0); + root->func("$contents", Proto::of("$contents"), + Value(root, INTRO_CONTENTS), 0); + root->func("$start", Proto::of("$start"), + Value(root, INTRO_START), 0); + root->func("$section", Proto::of("$section", ARG_VARIABLE), + Value(root, INTRO_SET_SECTION), 0); intro_help(root, Value(VOID)); diff --git a/compiler/parse.cpp b/compiler/parse.cpp index 8a1da6f..6e5b661 100644 --- a/compiler/parse.cpp +++ b/compiler/parse.cpp @@ -53,12 +53,12 @@ namespace basil { } } - void parse_enclosed(TokenView& view, vector& terms, TokenType terminator, u32 indent) { + SourceLocation parse_enclosed(TokenView& view, vector& terms, TokenType terminator, u32 indent) { vector tuple_elements; bool errored = false; while (view.peek().type != terminator) { while (view.peek().type == T_NEWLINE) view.read(); - if (!view.peek() && out_of_input(view)) return; + if (!view.peek() && out_of_input(view)) return NO_LOCATION; if (view.peek().type == terminator) break; terms.push(parse(view, indent)); if (view.peek().type == T_COMMA) { @@ -72,6 +72,7 @@ namespace basil { view.read(); } } + SourceLocation result = view.peek(); view.read(); if (tuple_elements.size() > 0) { tuple_elements.push(list_of(terms)); @@ -79,11 +80,12 @@ namespace basil { terms.push(Value("tuple-of")); for (const Value& v : tuple_elements) terms.push(v); } + return result; } void parse_block(TokenView& view, vector& terms, u32 prev_indent, u32 indent) { while (view.peek().column > prev_indent) { - if (view.peek().type != T_NEWLINE) terms.push(parse_line(view, indent, false)); + if (view.peek().type != T_NEWLINE) parse_line(terms, view, indent, false); if (view.peek().type == T_NEWLINE) { if (view.peek().column <= prev_indent && view.repl()) { view.read(); @@ -93,22 +95,18 @@ namespace basil { } if (!view.peek() && (!view.repl() || out_of_input(view))) return; } - if (view.peek().type == T_QUOTE && view.peek().column == prev_indent) { // continuation - u32 new_indent = view.peek().column; - parse_line(view, new_indent, true, terms); - } } Value apply_op(TokenView& view, Value lhs, Value rhs, TokenType op) { switch (op) { case T_COLON: { Value v = list_of(Value("annotate"), lhs, rhs); - v.set_location(lhs.loc()); + v.set_location(span(lhs.loc(), rhs.loc())); return v; } case T_DOT: { Value v = list_of(Value("at", SYMBOL), lhs, list_of(Value("quote", SYMBOL), rhs)); - v.set_location(lhs.loc()); + v.set_location(span(lhs.loc(), rhs.loc())); return v; } default: @@ -153,68 +151,72 @@ namespace basil { } case T_PLUS: { view.read(); - v = list_of(Value("+"), 0, parse_primary(view, indent)); - v.set_location(first); + Value operand = parse_primary(view, indent); + v = list_of(Value("+"), 0, operand); + v.set_location(span(first, operand.loc())); break; } case T_MINUS: { view.read(); - v = list_of(Value("-"), 0, parse_primary(view, indent)); - v.set_location(first); + Value operand = parse_primary(view, indent); + v = list_of(Value("-"), 0, operand); + v.set_location(span(first, operand.loc())); break; } case T_QUOTE: { view.read(); - v = list_of(Value("quote"), parse_primary(view, indent)); - v.set_location(first); + Value operand = parse_primary(view, indent); + v = list_of(Value("quote"), operand); + v.set_location(span(first, operand.loc())); break; } case T_COEFF: { i64 i = parse_int(view.read().value); - v = list_of(Value("*"), i, parse_primary(view, indent)); - v.set_location(first); + Value operand = parse_primary(view, indent); + v = list_of(Value("*"), i, operand); + v.set_location(span(first, operand.loc())); break; } case T_LPAREN: { view.read(); vector terms; - parse_enclosed(view, terms, T_RPAREN, indent); + auto end = parse_enclosed(view, terms, T_RPAREN, indent); for (const Value& v : terms) if (v.is_error()) return error(); v = list_of(terms); - v.set_location(first); + v.set_location(span(first, end)); break; } case T_LBRACK: { view.read(); vector terms; terms.push(Value("list-of")); - parse_enclosed(view, terms, T_RBRACK, indent); + auto end = parse_enclosed(view, terms, T_RBRACK, indent); for (const Value& v : terms) if (v.is_error()) return error(); v = list_of(terms); - v.set_location(first); + v.set_location(span(first, end)); break; } case T_LBRACE: { view.read(); vector terms; terms.push(Value("set-of")); - parse_enclosed(view, terms, T_RBRACE, indent); + auto end = parse_enclosed(view, terms, T_RBRACE, indent); for (const Value& v : terms) if (v.is_error()) return error(); v = list_of(terms); - v.set_location(first); + v.set_location(span(first, end)); break; } case T_PIPE: { view.read(); vector terms; - parse_enclosed(view, terms, T_PIPE, indent); + auto end = parse_enclosed(view, terms, T_PIPE, indent); for (const Value& v : terms) if (v.is_error()) return error(); v = cons(Value("splice"), list_of(terms)); - v.set_location(first); + v.set_location(span(first, end)); break; } default: @@ -226,12 +228,12 @@ namespace basil { view.read(); vector terms; terms.push(Value("list-of")); - parse_enclosed(view, terms, T_RBRACK, indent); + auto end = parse_enclosed(view, terms, T_RBRACK, indent); for (const Value& v : terms) if (v.is_error()) return error(); Value indices = terms.size() == 2 ? terms[1] : list_of(terms); v = list_of(Value("at", SYMBOL), v, indices); - v.set_location(first); + v.set_location(span(first, end)); } return v; } @@ -254,11 +256,12 @@ namespace basil { if (view.peek().type == T_NEWLINE) { view.read(); if (!view.peek() && (!view.repl() || out_of_input(view))) return; - if (view.peek().column > indent) parse_block(view, terms, indent, view.peek().column); - else if (view.peek().type == T_QUOTE && view.peek().column == indent) { // continuation - u32 new_indent = view.peek().column; - parse_line(view, new_indent, true, terms); - } else if (!consume_line) + if (view.peek().column > indent) { + vector body; + parse_block(view, body, indent, view.peek().column); + if (terms.size() > 0) terms.back() = cons(terms.back(), list_of(body)); + } + else if (!consume_line) view.rewind(); return; @@ -268,17 +271,18 @@ namespace basil { } } - Value parse_line(TokenView& view, u32 indent, bool consume_line) { + void parse_line(vector& outer_terms, TokenView& view, u32 indent, bool consume_line) { SourceLocation first = view.peek(); vector terms; if (view.peek().type == T_NEWLINE) { if (consume_line) view.read(); - return empty(); } parse_line(view, indent, consume_line, terms); - - Value v = list_of(terms); - v.set_location(first); - return v; + // Value line = terms.size() == 1 ? terms[0] : list_of(terms); + // if (!line.is_void()) { + // line.set_location(span(terms.front().loc(), terms.back().loc())); + // outer_terms.push(line); + // } + for (const Value& v : terms) outer_terms.push(v); } } // namespace basil \ No newline at end of file diff --git a/compiler/parse.h b/compiler/parse.h index b303ecb..426101d 100644 --- a/compiler/parse.h +++ b/compiler/parse.h @@ -8,7 +8,7 @@ namespace basil { Value parse(TokenView& view, u32 indent); - Value parse_line(TokenView& view, u32 indent, + void parse_line(vector& terms, TokenView& view, u32 indent, bool consume_line = true); } diff --git a/compiler/values.cpp b/compiler/values.cpp index a2b4df8..109f711 100644 --- a/compiler/values.cpp +++ b/compiler/values.cpp @@ -87,6 +87,10 @@ namespace basil { _data.rc = new MacroValue(env, b); } + Value::Value(ref env, const Builtin& b, const string& name) : Value(env, b) { + set_name(name); + } + Value::Value(FunctionValue* f, const Type* ftype) : _type(ftype) { _data.rc = f; } @@ -116,7 +120,7 @@ namespace basil { if (_type->kind() & GC_KIND_FLAG) _data.rc->dec(); } - Value::Value(const Value& other) : _type(other._type), _loc(other._loc) { + Value::Value(const Value& other) : _type(other._type), _loc(other._loc), _name(other._name) { _data.u = other._data.u; // copy over raw data if (_type->kind() & GC_KIND_FLAG) _data.rc->inc(); } @@ -126,6 +130,7 @@ namespace basil { if (_type->kind() & GC_KIND_FLAG) _data.rc->dec(); _type = other._type; _loc = other._loc; + _name = other._name; _data.u = other._data.u; // copy over raw data return *this; } @@ -347,7 +352,8 @@ namespace basil { } void Value::format(stream& io) const { - if (is_void()) write(io, "()"); + if (_name != -1) write(io, symbol_for(_name)); + else if (is_void()) write(io, "()"); else if (is_error()) write(io, "error"); else if (is_int()) @@ -581,55 +587,96 @@ namespace basil { vector vals; while (i) vals.push(&i->head()), i = i->tail().is_void() ? nullptr : &i->tail().get_list(); for (i64 i = i64(vals.size()) - 1; i >= 0; i --) l = new ListValue(vals[i]->clone(), l ? l : empty()); - return Value(l); + Value result = Value(l); + result.set_location(_loc); + result._name = _name; + return result; + } + else if (is_string()) { + Value result = Value(get_string(), STRING); + result.set_location(_loc); + result._name = _name; + return result; + } + else if (is_named()) { + Value result = Value(new NamedValue(get_named().get().clone()), type()); + result.set_location(_loc); + return result; + } + else if (is_sum()) { + Value result = Value(new SumValue(get_sum().value().clone()), type()); + result.set_location(_loc); + result._name = _name; + return result; } - else if (is_string()) - return Value(get_string(), STRING); - else if (is_named()) - return Value(new NamedValue(get_named().get().clone()), type()); - else if (is_sum()) - return Value(new SumValue(get_sum().value().clone()), type()); else if (is_intersect()) { map values; for (const auto& p : get_intersect()) values.put(p.first, p.second.clone()); - return Value(new IntersectValue(values), type()); + Value result = Value(new IntersectValue(values), type()); + result.set_location(_loc); + result._name = _name; + return result; } else if (is_product()) { vector values; for (const Value& v : get_product()) values.push(v.clone()); - return Value(new ProductValue(values)); + Value result = Value(new ProductValue(values)); + result.set_location(_loc); + result._name = _name; + return result; } else if (is_array()) { vector values; for (const Value& v : get_array()) values.push(v.clone()); - return Value(new ProductValue(values)); + Value result = Value(new ArrayValue(values)); + result.set_location(_loc); + result._name = _name; + return result; } else if (is_dict()) { map entries; for (const auto& p : get_dict()) entries.put(p.first.clone(), p.second.clone()); - return Value(new DictValue(entries)); + Value result = Value(new DictValue(entries)); + result.set_location(_loc); + result._name = _name; + return result; } else if (is_function()) { if (get_function().is_builtin()) { - return Value(new FunctionValue(get_function().get_env()->clone(), get_function().get_builtin(), + Value result = Value(new FunctionValue(get_function().get_env()->clone(), get_function().get_builtin(), get_function().name()), type()); + result.set_location(_loc); + result._name = _name; + return result; } else { - return Value(new FunctionValue(get_function().get_env()->clone(), get_function().args(), + Value result = Value(new FunctionValue(get_function().get_env()->clone(), get_function().args(), get_function().body().clone()), type()); + result.set_location(_loc); + result._name = _name; + return result; } } else if (is_alias()) return Value(new AliasValue(get_alias().value())); else if (is_macro()) { if (get_macro().is_builtin()) { - return Value(new MacroValue(get_macro().get_env()->clone(), get_macro().get_builtin())); + Value result = Value(new MacroValue(get_macro().get_env()->clone(), get_macro().get_builtin())); + result.set_location(_loc); + result._name = _name; + return result; } else { - return Value( + Value result = Value( new MacroValue(get_macro().get_env()->clone(), get_macro().args(), get_macro().body().clone())); + result.set_location(_loc); + result._name = _name; + return result; } } else if (is_runtime()) { // todo: ast cloning } else if (is_module()) { map members; for (auto& p : get_module().entries()) members[p.first] = p.second.clone(); - return new ModuleValue(members); + Value result = new ModuleValue(members); + result.set_location(_loc); + result._name = _name; + return result; } return *this; } @@ -642,6 +689,10 @@ namespace basil { _loc = loc; } + void Value::set_name(const string& name) { + _name = symbol_value(name); + } + SourceLocation Value::loc() const { return _loc; } @@ -1525,7 +1576,7 @@ namespace basil { } } - Value call(ref env, Value& callable, const Value& args) { + Value call(ref env, Value& callable, const Value& args, SourceLocation callsite) { if (!args.is_product()) { err(args.loc(), "Expected product value for arguments."); return error(); @@ -1563,7 +1614,8 @@ namespace basil { } if (ftypes.size() == 0) { - err(function.loc(), "No overload of '", function, "' matches argument types ", args.type(), "."); + err(function.loc(), "No overload of '", function, "' matches argument type", + args.get_product().size() == 1 ? " " : "s ", commalist(Value(args.type(), TYPE), true), "."); return error(); } if (ftypes.size() > 1) { @@ -1673,8 +1725,10 @@ namespace basil { if (fn.is_builtin()) { if (has_runtime && fn.get_builtin().should_lower()) for (Value& v : args_copy.get_product()) v = lower(v); - return has_runtime ? fn.get_builtin().compile(env, args_copy) + Value result = has_runtime ? fn.get_builtin().compile(env, args_copy) : fn.get_builtin().eval(env, args_copy); + result.set_location(callsite); + return result; } else { vector rtargs; ref fnenv = fn.get_env(); @@ -1710,11 +1764,12 @@ namespace basil { body = instantiate(function.loc(), fn, argst); } if (!body) return error(); - return new ASTCall(callable.loc(), body, rtargs); + return new ASTCall(callsite, body, rtargs); } else { Value result = eval(fnenv, fn.body()); for (auto& p : bindings) fnenv->find(p.first)->value = p.second; + result.set_location(callsite); return result; } } diff --git a/compiler/values.h b/compiler/values.h index 0348ed2..12c06e8 100644 --- a/compiler/values.h +++ b/compiler/values.h @@ -41,6 +41,7 @@ namespace basil { RC* rc; } _data; SourceLocation _loc; + i32 _name = -1; public: Value(); Value(const Type* type); @@ -56,6 +57,7 @@ namespace basil { Value(ArrayValue* a, const Type* type); Value(DictValue* d); Value(ref env, const Builtin& b); + Value(ref env, const Builtin& b, const string& name); Value(FunctionValue* f, const Type* ftype); Value(AliasValue* f); Value(MacroValue* f); @@ -150,6 +152,7 @@ namespace basil { Value clone() const; void set_location(SourceLocation loc); + void set_name(const string& name); SourceLocation loc() const; }; @@ -383,7 +386,7 @@ namespace basil { Value cast(const Value& val, const Type* type); Value annotate(const Value& val, const Value& type); Value as(const Value& v, const Value& t); - Value call(ref env, Value& function, const Value& arg); + Value call(ref env, Value& function, const Value& arg, SourceLocation callsite); Value display(const Value& arg); Value assign(ref env, const Value& dest, const Value& src); } diff --git a/std/math.bl b/std/math.bl index 2d80273..0e0b07e 100644 --- a/std/math.bl +++ b/std/math.bl @@ -1,10 +1,12 @@ -# infix 45 (x ^ y) -# if y < 0 1 / (x ^ -y) -# :elif y == 0 1 -# :else x * x ^ (y - 1) +def (x? squared) x * x +def (x? cubed) x * x * x -infix 45 (x squared) x * x -infix 45 (x cubed) x * x * x +def (x? factorial) do + if x == 0 1 else x - 1 factorial * x -infix (x factorial) - if x == 0 1 else x - 1 factorial * x \ No newline at end of file +def numbers module + def a 10 + def b 20 + +def functions module + def (inc x?) x + 1 \ No newline at end of file diff --git a/test/defs.bl b/test/defs.bl new file mode 100644 index 0000000..510a07d --- /dev/null +++ b/test/defs.bl @@ -0,0 +1,28 @@ +def x 1 + +# = 1 +display x + +def (id x?) x + +# = 2 +display (id 2) + +def (x? ++) x + 1 + +# = 3 +display 2 ++ + +def (f x? y?) where: + ... +do: + ... + ... + ... + +def (f x? y?) if x < 0 then: + ... +else: + ... + +def (if cond? [ifTrue?] else [ifFalse?]) \ No newline at end of file diff --git a/test/rational.bl b/test/rational.bl deleted file mode 100644 index 755b366..0000000 --- a/test/rational.bl +++ /dev/null @@ -1,24 +0,0 @@ -def Rational Rational of (int, int) -def (display r?: Rational) - display r.0 - display "/" - display r.1 - - r . 0 - (at r 0) - at :: any[] -> int -> any - at :: (any tuple) -> int -> any - - int, int, int - Pair(int, Pair(int, int)) - at :: (any, any) -> int -> any - any-tuple - -def (variadic x?: int... y?: string) - (int..., string) - (int, string) - (int, int, string) - (string) - -def x: Rational (1, 2) -display x \ No newline at end of file From 35719d8f98a586e4915de0c323cd4d30e4a6650a Mon Sep 17 00:00:00 2001 From: elucent <9532786+elucent@users.noreply.github.com> Date: Fri, 9 Apr 2021 01:36:00 -0400 Subject: [PATCH 17/17] Unscrambled files after merge conflict. --- compiler/builtin.cpp | 105 ------------ compiler/builtin.h | 9 - compiler/driver.cpp | 285 ------------------------------- compiler/env.cpp | 129 -------------- compiler/env.h | 66 +------- compiler/errors.cpp | 88 +++------- compiler/errors.h | 36 ---- compiler/main.cpp | 145 ---------------- compiler/parse.h | 6 - compiler/values.cpp | 20 --- compiler/values.h | 390 ------------------------------------------- 11 files changed, 27 insertions(+), 1252 deletions(-) diff --git a/compiler/builtin.cpp b/compiler/builtin.cpp index a50919a..2a48c38 100644 --- a/compiler/builtin.cpp +++ b/compiler/builtin.cpp @@ -40,7 +40,6 @@ namespace basil { #define ARG(n) args.get_product()[n] -<<<<<<< HEAD Builtin ADD_INT, ADD_SYMBOL, SUB, MUL, DIV, REM, AND, OR, XOR, NOT, EQUALS, NOT_EQUALS, LESS, GREATER, LESS_EQUAL, GREATER_EQUAL, @@ -50,12 +49,6 @@ namespace basil { STRCAT, SUBSTR, ANNOTATE, TYPEOF, LIST_TYPE, OF_TYPE_MACRO, OF_TYPE, ASSIGN, IF, EVAL, FLAG; -======= - Builtin ADD_INT, ADD_SYMBOL, SUB, MUL, DIV, REM, AND, OR, XOR, NOT, EQUALS, NOT_EQUALS, LESS, GREATER, LESS_EQUAL, - GREATER_EQUAL, IS_EMPTY, HEAD, TAIL, CONS, DICT_IN, DICT_NOT_IN, DISPLAY, READ_LINE, READ_WORD, READ_INT, - LENGTH, AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, AT_MODULE, AT_DICT, AT_DICT_TYPE, AT_DICT_LIST, - STRCAT, SUBSTR, ANNOTATE, TYPEOF, TYPEDEF, LIST_TYPE, OF_TYPE_MACRO, OF_TYPE, ASSIGN, IF; ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 static void init_builtins() { ADD_INT = { find(find(INT, INT), INT), @@ -217,7 +210,6 @@ namespace basil { }, nullptr // todo: runtime indexing }; -<<<<<<< HEAD AT_ARRAY_TYPE = { find(find(TYPE, INT), TYPE), [](ref env, const Value& args) -> Value { @@ -332,88 +324,6 @@ namespace basil { }, nullptr }; -======= - AT_ARRAY_TYPE = { find(find(TYPE, INT), TYPE), - [](ref env, const Value& args) -> Value { - return Value(find(ARG(0).get_type(), ARG(1).get_int()), TYPE); - }, - nullptr }; - AT_DYNARRAY_TYPE = { find(find(TYPE, VOID), TYPE), - [](ref env, const Value& args) -> Value { - return Value(find(ARG(0).get_type()), TYPE); - }, - nullptr }; - AT_DICT = { find(find(find(ANY, ANY), ANY), ANY), - [](ref env, const Value& args) -> Value { return *(ARG(0).get_dict())[ARG(1)]; }, nullptr }; - AT_DICT_LIST = { find(find(find(ANY, ANY), find(ANY)), ANY), - [](ref env, const Value& args) -> Value { - vector vals; - for (const Value& key : to_vector(ARG(1))) vals.push(*ARG(0).get_dict()[key]); - return Value(new ArrayValue(vals)); - }, - nullptr }; - AT_DICT_TYPE = { find(find(TYPE, TYPE), TYPE), - [](ref env, const Value& args) -> Value { - return Value(find(ARG(1).get_type(), ARG(0).get_type()), TYPE); - }, - nullptr }; - AT_MODULE = { find(find(MODULE, SYMBOL), ANY), - [](ref env, const Value& args) -> Value { - if (!ARG(0).get_module().has(ARG(1).get_symbol())) { - err(ARG(1).loc(), "Module does not contain member '", symbol_for(ARG(1).get_symbol()), - "'."); - return error(); - } - return ARG(0).get_module().entry(ARG(1).get_symbol()); - }, - nullptr }; - ANNOTATE = { find(find(ANY, TYPE), ANY), - [](ref env, const Value& args) -> Value { - if (!ARG(0).type()->coerces_to(ARG(1).get_type())) { - err(ARG(0).loc(), "Could not unify value of type '", ARG(0).type(), "' with type '", - ARG(1).get_type(), "'."); - return error(); - } - return cast(ARG(0), ARG(1).get_type()); - }, - [](ref env, const Value& args) -> Value { - return new ASTAnnotate(ARG(0).loc(), lower(ARG(0)).get_runtime(), ARG(1).get_type()); - }, - NO_AUTO_LOWER }; - TYPEOF = { find(find(ANY), TYPE), - [](ref env, const Value& args) -> Value { return Value(ARG(0).type(), TYPE); }, nullptr }; - TYPEDEF = { find(2), - [](ref env, const Value& args) -> Value { - return list_of(Value("def", SYMBOL), ARG(0), - list_of(Value("#of", SYMBOL), list_of(Value("quote", SYMBOL), ARG(0)), ARG(1))); - }, - nullptr }; - LIST_TYPE = { find(find(TYPE), TYPE), - [](ref env, const Value& args) -> Value { - return Value(find(ARG(0).get_type()), TYPE); - }, - nullptr }; - OF_TYPE_MACRO = { find(2), - [](ref env, const Value& args) -> Value { - return list_of(Value("#of", SYMBOL), list_of(Value("quote", SYMBOL), ARG(0)), ARG(1)); - }, - nullptr }; - OF_TYPE = { find(find(SYMBOL, TYPE), TYPE), - [](ref env, const Value& args) -> Value { - return Value(find(symbol_for(ARG(0).get_symbol()), ARG(1).get_type()), TYPE); - }, - nullptr }; - IF = { find(find(BOOL, ANY, ANY), ANY), - [](ref env, const Value& args) -> Value { return eval(env, ARG(ARG(0).get_bool() ? 1 : 2)); }, - [](ref env, const Value& args) -> Value { - Value left = eval(env, ARG(1)), right = eval(env, ARG(2)); - if (left.is_error() || right.is_error()) return error(); - if (!left.is_runtime()) left = lower(left); - if (!right.is_runtime()) right = lower(right); - return new ASTIf(ARG(0).loc(), ARG(0).get_runtime(), left.get_runtime(), right.get_runtime()); - }, - NO_AUTO_LOWER }; ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 } static bool inited = false; @@ -477,7 +387,6 @@ namespace basil { env->func("in", Proto::of(ARG_VARIABLE, "in", ARG_VARIABLE), Value(env, DICT_IN, "in"), 60); // env->infix("not", Value(env, DICT_NOT_IN), 2, 60); -<<<<<<< HEAD env->func("display", Proto::of("display", ARG_VARIABLE), Value(env, DISPLAY, "display"), 0); env->func("at", Proto::of(ARG_VARIABLE, "at", ARG_VARIABLE), @@ -499,19 +408,5 @@ namespace basil { Value(env, EVAL, "eval"), 0); env->func("$flag", Proto::of("$flag", ARG_VARIABLE), Value(env, FLAG, "flag"), 0); -======= - env->def("display", Value(env, DISPLAY), 1); - env->infix("at", - cases(env, &AT_INT, &AT_LIST, &AT_ARRAY_TYPE, &AT_DYNARRAY_TYPE, &AT_MODULE, &AT_DICT, &AT_DICT_TYPE, - &AT_DICT_LIST), - 2, 120); - env->def("annotate", Value(env, ANNOTATE), 2); - env->def("typeof", Value(env, TYPEOF), 1); - env->def_macro("typedef", Value(env, TYPEDEF), 2); - env->infix_macro("of", Value(env, OF_TYPE_MACRO), 2, 20); - env->def("#of", Value(env, OF_TYPE), 2); - env->infix("list", Value(env, LIST_TYPE), 1, 80); - env->infix("#?", Value(env, IF), 3, 2); ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 } } // namespace basil \ No newline at end of file diff --git a/compiler/builtin.h b/compiler/builtin.h index fcc1f0c..2450cf4 100644 --- a/compiler/builtin.h +++ b/compiler/builtin.h @@ -35,7 +35,6 @@ namespace basil { extern void define_builtins(ref env); -<<<<<<< HEAD extern Builtin ADD_INT, ADD_SYMBOL, SUB, MUL, DIV, REM, AND, OR, XOR, NOT, @@ -47,13 +46,5 @@ namespace basil { ASSIGN, IF, EVAL, FLAG; } -======= - extern Builtin ADD_INT, ADD_SYMBOL, SUB, MUL, DIV, REM, AND, OR, XOR, NOT, EQUALS, NOT_EQUALS, LESS, GREATER, - LESS_EQUAL, GREATER_EQUAL, IS_EMPTY, HEAD, TAIL, CONS, DICT_PUT, DICT_IN, DICT_NOT_IN, DICT_REMOVE, DISPLAY, - READ_LINE, READ_WORD, READ_INT, LENGTH, AT_INT, AT_LIST, AT_ARRAY_TYPE, AT_DYNARRAY_TYPE, AT_DICT_TYPE, - AT_MODULE, AT_DICT, AT_DICT_LIST, STRCAT, SUBSTR, ANNOTATE, TYPEOF, TYPEDEF, LIST_TYPE, OF_TYPE_MACRO, OF_TYPE, - ASSIGN, IF; -} // namespace basil ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 #endif \ No newline at end of file diff --git a/compiler/driver.cpp b/compiler/driver.cpp index cc1fdd2..18dcf22 100644 --- a/compiler/driver.cpp +++ b/compiler/driver.cpp @@ -9,7 +9,6 @@ #include "values.h" namespace basil { -<<<<<<< HEAD static bool _print_tokens = false, _print_parsed = false, _print_ast = false, @@ -296,287 +295,3 @@ namespace basil { return 0; } } -======= - static bool _print_tokens = false, _print_parsed = false, _print_ast = false, _print_ir = false, _print_asm = false, - _compile_only = false; - - void print_tokens(bool should) { - _print_tokens = should; - } - - void print_parsed(bool should) { - _print_parsed = should; - } - - void print_ast(bool should) { - _print_ast = should; - } - - void print_ir(bool should) { - _print_ir = should; - } - - void print_asm(bool should) { - _print_asm = should; - } - - void compile_only(bool should) { - _compile_only = should; - } - - bool is_compile_only() { - return _compile_only; - } - - vector lex(Source::View& view) { - vector tokens; - while (view.peek()) tokens.push(scan(view)); - - if (_print_tokens) { - print(BOLDYELLOW); - for (const Token& tok : tokens) print(tok, " "); - println(RESET, "\n"); - } - return tokens; - } - - Value parse(TokenView& view) { - vector results; - TokenView end = view; - while (end) end.read(); - while (view.peek()) { - Value line = parse_line(view, view.peek().column); - if (!line.is_void()) results.push(line); - } - if (_print_tokens) { - print(BOLDYELLOW); - while (end) print(end.read(), " "); - println(RESET); - } - if (_print_parsed) { - print(BOLDGREEN); - for (const Value& v : results) println(v); - println(RESET); - } - return cons(string("do"), list_of(results)); - } - - ref create_global_env() { - ref root = create_root_env(); - ref global_env = newref(root); - return global_env; - } - - void compile(Value value, Object& object, Function& fn) { - fn.allocate(); - fn.emit(object); - emit_constants(object); - if (error_count()) return; - - if (_print_asm) { - print(BOLDRED); - byte_buffer code = object.code(); - while (code.size()) printf("%02x ", code.read()); - println(RESET, "\n"); - } - } - - void generate(Value value, Function& fn) { - Location last = loc_none(); - if (value.is_runtime()) last = value.get_runtime()->emit(fn); - if (last.type != LOC_NONE) fn.add(new RetInsn(last)); - if (error_count()) return; - - if (_print_ir) { - print(BOLDMAGENTA); - print(fn); - println(RESET, "\n"); - } - } - - void compile(Value value, Object& object) { - Function main_fn("_start"); - Location last = loc_none(); - if (value.is_runtime()) last = value.get_runtime()->emit(main_fn); - if (last.type != LOC_NONE) main_fn.add(new RetInsn(last)); - if (error_count()) return; - - if (_print_ir) { - print(BOLDMAGENTA); - print(main_fn); - println(RESET, "\n"); - } - - compile(value, object, main_fn); - } - - void jit_print(Value value, const Object& object) { - auto main_jit = object.find(jasmine::global("main")); - if (main_jit) { - u64 result = ((u64(*)())main_jit)(); - const Type* t = value.get_runtime()->type(); - if (t == VOID) return; - print("= "); - - if (t == INT) print((i64)result); - else if (t == SYMBOL) - print(symbol_for(result)); - else if (t == BOOL) - print((bool)result); - else if (t == STRING) - print('"', (const char*)result, '"'); - else if (t->kind() == KIND_LIST) - display_native_list(t, (void*)result); - println(""); - } - } - - int execute(Value value, const Object& object) { - auto main_jit = object.find(jasmine::global("_start")); - if (main_jit) return ((i64(*)())main_jit)(); - return 1; - } - - Value repl(ref global, Source& src, Function& mainfn) { - print("? "); - auto view = src.expand(_stdin); - auto tokens = lex(view); - if (error_count()) return print_errors(_stdout, src), error(); - - TokenView tview(tokens, src, true); - Value program = parse(tview); - if (error_count()) return print_errors(_stdout, src), error(); - - prep(global, program); - Value result = eval(global, program); - if (error_count()) return print_errors(_stdout, src), error(); - - if (!result.is_runtime()) { - if (!result.is_void()) println(BOLDBLUE, "= ", result, RESET, "\n"); - return result; - } - if (_print_ast) println(BOLDCYAN, result.get_runtime(), RESET, "\n"); - - jasmine::Object object; - generate(result, mainfn); - compile(result, object, mainfn); - add_native_functions(object); - object.load(); - if (error_count()) return print_errors(_stdout, src), error(); - - print(BOLDBLUE); - jit_print(result, object); - println(RESET); - return result; - } - - ref load(Source& src) { - auto view = src.begin(); - auto tokens = lex(view); - if (error_count()) return print_errors(_stdout, src), nullptr; - - TokenView tview(tokens, src); - Value program = parse(tview); - if (error_count()) return print_errors(_stdout, src), nullptr; - - ref global = create_global_env(); - - prep(global, program); - Value result = eval(global, program); - if (error_count()) return print_errors(_stdout, src), nullptr; - - return global; - } - - int run(Source& src) { - auto view = src.begin(); - auto tokens = lex(view); - if (error_count()) return print_errors(_stdout, src), 1; - - TokenView tview(tokens, src); - Value program = parse(tview); - if (error_count()) return print_errors(_stdout, src), 1; - - ref global = create_global_env(); - - prep(global, program); - Value result = eval(global, program); - if (error_count()) return print_errors(_stdout, src), 1; - - if (!result.is_runtime()) return 0; - if (_print_ast) println(BOLDCYAN, result.get_runtime(), RESET, "\n"); - - jasmine::Object object; - compile(result, object); - add_native_functions(object); - object.load(); - if (error_count()) return print_errors(_stdout, src), 1; - - return execute(result, object); - } - - string change_ending(string s, string e) { - string d = ""; - int last = 0; - for (int i = 0; i < s.size(); i++) - if (s[i] == '.') last = i; - for (int i = 0; i < last; i++) d += s[i]; - return d + e; - } - - vector instantiations(ref env) { - vector insts; - for (const auto& p : *env) { - if (p.second.value.is_function()) { - const FunctionValue& fn = p.second.value.get_function(); - if (fn.instantiations()) - for (auto& i : *fn.instantiations()) { insts.push(i.second); } - } - } - return insts; - } - - int build(Source& src, const char* filename) { - string dest = change_ending(filename, ".o"); - auto view = src.begin(); - auto tokens = lex(view); - if (error_count()) return print_errors(_stdout, src), 1; - - TokenView tview(tokens, src); - Value program = parse(tview); - if (error_count()) return print_errors(_stdout, src), 1; - - ref global = create_global_env(); - - prep(global, program); - Value result = eval(global, program); - if (error_count()) return print_errors(_stdout, src), 1; - - auto insts = instantiations(global); - if (result.is_runtime()) { - if (_print_ast) println(BOLDCYAN, result.get_runtime(), RESET, "\n"); - - jasmine::Object object; - compile(result, object); - if (error_count()) return print_errors(_stdout, src), 1; - object.writeELF((const char*)dest.raw()); - } else if (insts.size() > 0) { - jasmine::Object object; - Function container(string(".") + filename); - for (Value f : insts) f.get_runtime()->emit(container); - if (_print_ir) { - print(BOLDMAGENTA); - print(container); - println(RESET, "\n"); - } - container.allocate(); - container.emit(object, true); - emit_constants(object); - if (error_count()) return print_errors(_stdout, src), 1; - object.writeELF((const char*)dest.raw()); - } - - return 0; - } -} // namespace basil ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 diff --git a/compiler/env.cpp b/compiler/env.cpp index 18d2407..e981d3a 100644 --- a/compiler/env.cpp +++ b/compiler/env.cpp @@ -1,7 +1,6 @@ #include "env.h" namespace basil { -<<<<<<< HEAD // Arg @@ -175,131 +174,3 @@ namespace basil { return _runtime; } } -======= - Def::Def(bool is_macro_in, bool is_procedure_in, bool is_infix_in, u8 arity_in, u8 precedence_in) - : Def(Value(), is_macro_in, is_procedure_in, is_infix_in, arity_in, precedence_in) {} - - Def::Def(Value value_in, bool is_macro_in, bool is_procedure_in, bool is_infix_in, u8 arity_in, u8 precedence_in) - : value(value_in), is_macro(is_macro_in), is_proc(is_procedure_in), is_infix(is_infix_in), arity(arity_in), - precedence(precedence_in) {} - - bool Def::is_procedure() const { - return is_proc && !is_macro; - } - - bool Def::is_variable() const { - return !is_proc && !is_macro; - } - - bool Def::is_macro_procedure() const { - return is_proc && is_macro; - } - - bool Def::is_macro_variable() const { - return !is_proc && is_macro; - } - - Env::Env(const ref& parent) : _parent(parent), _runtime(false) {} - - void Env::def(const string& name) { - _defs[name] = Def(false, false, false); - } - - void Env::def_macro(const string& name) { - _defs[name] = Def(true, false, false); - } - - void Env::def(const string& name, u8 arity) { - _defs[name] = Def(false, true, false, arity); - } - - void Env::def_macro(const string& name, u8 arity) { - _defs[name] = Def(true, true, false, arity); - } - - void Env::def(const string& name, const Value& value) { - _defs[name] = Def(value, false, false, false); - } - - void Env::def(const string& name, const Value& value, u8 arity) { - _defs[name] = Def(value, false, true, false, arity); - } - - void Env::def_macro(const string& name, const Value& value) { - _defs[name] = Def(value, true, false, false); - } - - void Env::def_macro(const string& name, const Value& value, u8 arity) { - _defs[name] = Def(value, true, true, false, arity); - } - - void Env::infix(const string& name, u8 arity, u8 precedence) { - _defs[name] = Def(false, true, true, arity, precedence); - } - - void Env::infix(const string& name, const Value& value, u8 arity, u8 precedence) { - _defs[name] = Def(value, false, true, true, arity, precedence); - } - - void Env::infix_macro(const string& name, u8 arity, u8 precedence) { - _defs[name] = Def(true, true, true, arity, precedence); - } - - void Env::infix_macro(const string& name, const Value& value, u8 arity, u8 precedence) { - _defs[name] = Def(value, true, true, true, arity, precedence); - } - - const Def* Env::find(const string& name) const { - auto it = _defs.find(name); - if (it == _defs.end()) return _parent ? _parent->find(name) : nullptr; - else - return &it->second; - } - - Def* Env::find(const string& name) { - auto it = _defs.find(name); - if (it == _defs.end()) return _parent ? _parent->find(name) : nullptr; - else - return &it->second; - } - - void Env::format(stream& io) const { - write(io, "{"); - bool first = true; - for (auto& def : _defs) write(io, first ? "" : " ", def.first), first = false; - write(io, "}"); - if (_parent) write(io, " -> "), _parent->format(io); - } - - ref Env::clone() const { - ref new_ref = newref(_parent); - for (auto& p : _defs) new_ref->_defs.put(p.first, p.second); - new_ref->_runtime = _runtime; - return new_ref; - } - - map::const_iterator Env::begin() const { - return _defs.begin(); - } - - map::const_iterator Env::end() const { - return _defs.end(); - } - - void Env::import(ref env) { - for (auto& p : env->_defs) _defs.put(p.first, p.second); - } - - void Env::import_single(const string& name, const Def& d) { - _defs.put(name, d); - } - - void Env::make_runtime() { - _runtime = true; - } - - bool Env::is_runtime() const { - return _runtime; - } -} // namespace basil ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 diff --git a/compiler/env.h b/compiler/env.h index eee718b..6a99ba2 100644 --- a/compiler/env.h +++ b/compiler/env.h @@ -6,15 +6,10 @@ #include "util/defs.h" #include "util/hash.h" #include "util/rc.h" -<<<<<<< HEAD #include "util/vec.h" -======= -#include "util/str.h" ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 #include "values.h" namespace basil { -<<<<<<< HEAD enum ArgType { ARG_VARIABLE, ARG_KEYWORD, @@ -80,37 +75,14 @@ namespace basil { bool is_macro_variable() const; bool is_infix() const; }; -======= - struct Def { - Value value; - bool is_macro; // is the definition a macro alias or procedure? - bool is_infix; // is the definition for an infix procedure? - bool is_proc; // is the definition a scalar or procedure? - u8 arity; // number of arguments taken by a procedure. - u8 precedence; // precedence of infix procedure - SSAIdent ident; - Location location; - Def(bool is_macro_in = false, bool is_procedure_in = false, bool is_infix_in = false, u8 arity_in = 0, - u8 precedence_in = 0); - Def(Value value_in, bool is_procedure_in = false, bool is_macro_in = false, bool is_infix_in = false, - u8 arity_in = 0, u8 precedence_in = 0); - bool is_procedure() const; - bool is_variable() const; - bool is_macro_procedure() const; - bool is_macro_variable() const; - }; - - class Env { - map _defs; - ref _parent; - bool _runtime; ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 - - public: - Env(const ref& parent = nullptr); + class Env { + map _defs; + ref _parent; + bool _runtime; + public: + Env(const ref& parent = nullptr); -<<<<<<< HEAD void var(const string& name); void func(const string& name, const Proto& proto, u8 precedence); void var(const string& name, const Value& value); @@ -131,31 +103,5 @@ namespace basil { bool is_runtime() const; }; } -======= - void def(const string& name); - void def(const string& name, u8 arity); - void def_macro(const string& name); - void def_macro(const string& name, u8 arity); - void def(const string& name, const Value& value); - void def(const string& name, const Value& value, u8 arity); - void def_macro(const string& name, const Value& value); - void def_macro(const string& name, const Value& value, u8 arity); - void infix(const string& name, u8 arity, u8 precedence); - void infix(const string& name, const Value& value, u8 arity, u8 precedence); - void infix_macro(const string& name, u8 arity, u8 precedence); - void infix_macro(const string& name, const Value& value, u8 arity, u8 precedence); - const Def* find(const string& name) const; - Def* find(const string& name); - void format(stream& io) const; - ref clone() const; - map::const_iterator begin() const; - map::const_iterator end() const; - void import(ref env); - void import_single(const string& name, const Def& d); - void make_runtime(); - bool is_runtime() const; - }; -} // namespace basil ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 #endif \ No newline at end of file diff --git a/compiler/errors.cpp b/compiler/errors.cpp index 5bdacc7..21ac727 100644 --- a/compiler/errors.cpp +++ b/compiler/errors.cpp @@ -1,14 +1,10 @@ #include "errors.h" -<<<<<<< HEAD #include "values.h" #include "util/vec.h" -======= ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 #include "lex.h" #include "util/vec.h" namespace basil { -<<<<<<< HEAD SourceLocation::SourceLocation(): line_start(0), line_end(0), column_start(0), column_end(0) {} @@ -55,55 +51,35 @@ namespace basil { while (b) s += b.read(); return s; } -======= - SourceLocation::SourceLocation() : line(0), column(0), length(0) {} - SourceLocation::SourceLocation(u32 line_in, u16 column_in, u16 length_in) - : line(line_in), column(column_in), length(length_in) {} + struct Error { + SourceLocation loc; + string message; + }; - SourceLocation::SourceLocation(const Token& token) - : line(token.line), column(token.column), length(token.value.size()) {} + static vector errors; + static i64 silenced = 0; - const SourceLocation NO_LOCATION = {0, 0, 0}; ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 - - struct Error { - SourceLocation loc; - string message; - }; - - static vector errors; - static i64 silenced = 0; - - void err(SourceLocation loc, const string& message) { - if (!silenced) errors.push({loc, message}); - } + void err(SourceLocation loc, const string& message) { + if (!silenced) errors.push({loc, message}); + } - u32 error_count() { - return errors.size(); - } + u32 error_count() { + return errors.size(); + } - void clear_errors() { - errors.clear(); - } + void clear_errors() { + errors.clear(); + } -<<<<<<< HEAD void print_errors(stream& io) { for (const Error& e : errors) { if (!e.loc.empty()) write(io, "[", e.loc.line_start + 1, ":", e.loc.column_start + 1, "] "); writeln(io, e.message); -======= - void print_errors(stream& io) { - for (const Error& e : errors) { - if (e.loc.length != 0) write(io, "[", e.loc.line + 1, ":", e.loc.column + 1, "] "); - writeln(io, e.message); - } - clear_errors(); ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 } + } -<<<<<<< HEAD void print_errors(stream& io, const Source& src) { for (const Error& e : errors) { if (!e.loc.empty()) @@ -120,33 +96,11 @@ namespace basil { for (; i < first; i ++) write(io, ' '); for (; i < last; i ++) write(io, '^'); writeln(io, ""); -======= - void print_errors(stream& io, const Source& src) { - for (const Error& e : errors) { - if (e.loc.length != 0) write(io, "[", e.loc.line + 1, ":", e.loc.column + 1, "] "); - writeln(io, e.message); - - if (e.loc.length == 0) continue; // skip source printing if no location - const auto& line = src.line(e.loc.line); - if (line.back() == '\n') write(io, '\t', line); - else - writeln(io, '\t', line); - u32 first = e.loc.column, last = e.loc.column + e.loc.length; - u32 i = 0; - write(io, '\t'); - for (; i < first; i++) write(io, ' '); - for (; i < last; i++) write(io, '^'); - writeln(io, ""); - } - } - - void silence_errors() { - silenced++; ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 } + } - void unsilence_errors() { - silenced--; - if (silenced < 0) silenced = 0; - } + void unsilence_errors() { + silenced--; + if (silenced < 0) silenced = 0; + } } // namespace basil \ No newline at end of file diff --git a/compiler/errors.h b/compiler/errors.h index ae76d4f..40c6464 100644 --- a/compiler/errors.h +++ b/compiler/errors.h @@ -7,7 +7,6 @@ #include "util/str.h" namespace basil { -<<<<<<< HEAD class Token; class Source; class Value; @@ -49,40 +48,5 @@ namespace basil { basil::err(loc, message); } } -======= - class Token; - class Source; - - struct SourceLocation { - u32 line; - u16 column, length; - - SourceLocation(); - SourceLocation(u32 line_in, u16 column_in, u16 length_in = 1); - SourceLocation(const Token& token); - // expand later with other types of objects - }; - - // has length of zero, indicates position not in source - extern const SourceLocation NO_LOCATION; - - void err(SourceLocation loc, const string& message); - u32 error_count(); - void clear_errors(); - void print_errors(stream& io); - void print_errors(stream& io, const Source& src); - void silence_errors(); - void unsilence_errors(); - - template - void err(SourceLocation loc, Args... args) { - buffer b; - write(b, args...); - string message; - while (b.peek()) message += b.read(); - basil::err(loc, message); - } -} // namespace basil ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 #endif \ No newline at end of file diff --git a/compiler/main.cpp b/compiler/main.cpp index 62e0d78..8860a96 100644 --- a/compiler/main.cpp +++ b/compiler/main.cpp @@ -9,7 +9,6 @@ using namespace basil; void print_banner() { -<<<<<<< HEAD println(""); println("┌────────────────────────────────────────┐"); println("│ │"); @@ -18,15 +17,6 @@ void print_banner() { println("│ │"); println("└────────────────────────────────────────┘"); println(RESET); -======= - println(""); - println("┌────────────────────────────────────────┐"); - println("│ │"); - println("│ ", BOLDGREEN, R"(𝐵𝑎𝑠𝑖𝑙)", RESET, " — version 0.1 │"); - println("│ │"); - println("└────────────────────────────────────────┘"); - println(RESET); ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 } static bool repl_done = false; @@ -78,7 +68,6 @@ Value repl_help(ref env, const Value& args) { int intro(); int main(int argc, char** argv) { -<<<<<<< HEAD Builtin REPL_HELP(find(find(), VOID), repl_help, nullptr), REPL_QUIT(find(find(), VOID), repl_quit, nullptr), PRINT_TOKENS(find(find(BOOL), VOID), print_tokens, nullptr), @@ -153,70 +142,6 @@ int main(int argc, char** argv) { println(" - basil exec => executes ."); println(" - basil build => compiles to object."); println(""); -======= - Builtin REPL_HELP(find(find(), VOID), repl_help, nullptr), - REPL_QUIT(find(find(), VOID), repl_quit, nullptr), - PRINT_TOKENS(find(find(BOOL), VOID), print_tokens, nullptr), - PRINT_PARSE(find(find(BOOL), VOID), print_parse, nullptr), - PRINT_AST(find(find(BOOL), VOID), print_ast, nullptr), - PRINT_IR(find(find(BOOL), VOID), print_ir, nullptr), - PRINT_ASM(find(find(BOOL), VOID), print_asm, nullptr); - - if (argc == 1) { // repl mode - print_banner(); - println(BOLDGREEN, "Enter any Basil expression at the prompt, or '", RESET, "$help", BOLDGREEN, - "' for a list of commands!", RESET); - println(""); - Source src; - - ref root = create_root_env(); - root->def("$help", Value(root, REPL_HELP), 0); - root->def("$quit", Value(root, REPL_QUIT), 0); - root->def("$print-tokens", Value(root, PRINT_TOKENS), 1); - root->def("$print-parse", Value(root, PRINT_PARSE), 1); - root->def("$print-ast", Value(root, PRINT_AST), 1); - root->def("$print-ir", Value(root, PRINT_IR), 1); - root->def("$print-asm", Value(root, PRINT_ASM), 1); - ref global = newref(root); - Function main_fn("main"); - - while (!repl_done) repl(global, src, main_fn), clear_errors(); - return 0; - } else if (string(argv[1]) == "intro") { - return intro(); - } else if (argc == 3 && string(argv[1]) == "run") { - Source src(argv[2]); - return run(src); - } else if (argc == 3 && string(argv[1]) == "build") { - Source src(argv[2]); - return build(src, argv[2]); - } else if (argc > 2 && string(argv[1]) == "exec") { - Source src; - string code; - for (int i = 2; i < argc; i++) { - code += argv[i]; - code += ' '; - } - src.add_line(code); - return run(src); - } else if (string(argv[1]) != "help") { - Source src(argv[1]); - return run(src); - } - - print_banner(); - - println(BOLDGREEN, "Welcome to the Basil programming language!", RESET); - println(""); - println("Commands: "); - println(" - basil => starts REPL."); - println(" - basil => executes ."); - println(" - basil help => prints usage information."); - println(" - basil intro => runs interactive introduction."); - println(" - basil exec => executes ."); - println(" - basil build => compiles to object."); - println(""); ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 } static bool running_intro = false; @@ -952,7 +877,6 @@ Bye for now! } int intro() { -<<<<<<< HEAD srand(time(0)); init_intro_sections(); @@ -1035,73 +959,4 @@ int intro() { } return 0; -======= - srand(time(0)); - init_intro_sections(); - - print_banner(); - println(BOLDGREEN, "Welcome to the interactive Basil tour!", RESET); - println(BOLDGREEN, "Press Enter twice after entering a command to submit it.", RESET); - - Source src; - - Builtin REPL_QUIT(find(find(), VOID), repl_quit, nullptr), - INTRO_HELP(find(find(), VOID), intro_help, nullptr), - INTRO_CONTENTS(find(find(), VOID), intro_contents, nullptr), - INTRO_START(find(find(), VOID), intro_start, nullptr), - INTRO_SET_SECTION(find(find(INT), VOID), intro_set_section, nullptr); - - ref root = create_root_env(); - root->def("$quit", Value(root, REPL_QUIT), 0); - root->def("$help", Value(root, INTRO_HELP), 0); - root->def("$contents", Value(root, INTRO_CONTENTS), 0); - root->def("$start", Value(root, INTRO_START), 0); - root->def("$section", Value(root, INTRO_SET_SECTION), 1); - - intro_help(root, Value(VOID)); - - const char* congratulations[] = {"Awesome!", "Great!", "Nice!", "Cool!", "You did it!", "You got it!"}; - - while (!repl_done) { - ref global = newref(root); - Function main_fn("main"); - - bool is_running_intro = running_intro; - if (is_running_intro) { - const IntroStep& step = intro_sections[intro_section].steps[intro_section_index]; - if (step.title) println(intro_section + 1, ". ", BOLDGREEN, step.title, RESET, '\n'); - println(BOLDWHITE, step.text, RESET); - println(ITALICWHITE, step.instruction, RESET); - } - - if (is_running_intro) { - bool correct = false; - - while (!correct && !repl_done) { - Value result = repl(global, src, main_fn); - - const IntroStep& step = intro_sections[intro_section].steps[intro_section_index]; - if (result.is_runtime() && step.expected.is_runtime()) correct = result.type() == step.expected.type(); - else - correct = result == step.expected; - - if (!correct) - println(ITALICRED, "Expected '", step.expected, "', but given '", result, "'.", RESET, '\n'); - else { - println(ITALICWHITE, congratulations[rand() % 6], RESET); - intro_section_index++; - if (intro_section_index >= intro_sections[intro_section].steps.size()) - intro_section++, intro_section_index = 0; - println(""); - usleep(1000000); - println("⸻", "\n"); - if (intro_section >= intro_sections.size()) repl_done = true, print_intro_conclusion(); - } - } - } else - repl(global, src, main_fn); - } - - return 0; ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 } diff --git a/compiler/parse.h b/compiler/parse.h index bea8549..7f7b9f5 100644 --- a/compiler/parse.h +++ b/compiler/parse.h @@ -7,15 +7,9 @@ #include "values.h" namespace basil { -<<<<<<< HEAD Value parse(TokenView& view, u32 indent); void parse_line(vector& terms, TokenView& view, u32 indent, bool consume_line = true); } -======= - Value parse(TokenView& view, u32 indent); - Value parse_line(TokenView& view, u32 indent, bool consume_line = true); -} // namespace basil ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 #endif \ No newline at end of file diff --git a/compiler/values.cpp b/compiler/values.cpp index 60786cb..18916bf 100644 --- a/compiler/values.cpp +++ b/compiler/values.cpp @@ -584,7 +584,6 @@ namespace basil { const ListValue* i = &get_list(); vector vals; while (i) vals.push(&i->head()), i = i->tail().is_void() ? nullptr : &i->tail().get_list(); -<<<<<<< HEAD for (i64 i = i64(vals.size()) - 1; i >= 0; i --) l = new ListValue(vals[i]->clone(), l ? l : empty()); Value result = Value(l); result.set_location(_loc); @@ -608,16 +607,6 @@ namespace basil { result._name = _name; return result; } -======= - for (i64 i = i64(vals.size()) - 1; i >= 0; i--) l = new ListValue(vals[i]->clone(), l ? l : empty()); - return Value(l); - } else if (is_string()) - return Value(get_string(), STRING); - else if (is_named()) - return Value(new NamedValue(get_named().get().clone()), type()); - else if (is_sum()) - return Value(new SumValue(get_sum().value().clone()), type()); ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 else if (is_intersect()) { map values; for (const auto& p : get_intersect()) values.put(p.first, p.second.clone()); @@ -1730,14 +1719,10 @@ namespace basil { if (fn.is_builtin()) { if (has_runtime && fn.get_builtin().should_lower()) for (Value& v : args_copy.get_product()) v = lower(v); -<<<<<<< HEAD Value result = has_runtime ? fn.get_builtin().compile(env, args_copy) : fn.get_builtin().eval(env, args_copy); result.set_location(callsite); return result; -======= - return has_runtime ? fn.get_builtin().compile(env, args_copy) : fn.get_builtin().eval(env, args_copy); ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 } else { vector rtargs; ref fnenv = fn.get_env(); @@ -1772,14 +1757,9 @@ namespace basil { body = instantiate(function.loc(), fn, argst); } if (!body) return error(); -<<<<<<< HEAD return new ASTCall(callsite, body, rtargs); } else { -======= - return new ASTCall(callable.loc(), body, rtargs); - } else { ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 Value result = eval(fnenv, fn.body()); for (auto& p : bindings) fnenv->find(p.first)->value = p.second; result.set_location(callsite); diff --git a/compiler/values.h b/compiler/values.h index 8cba010..50f572d 100644 --- a/compiler/values.h +++ b/compiler/values.h @@ -10,7 +10,6 @@ #include "util/vec.h" namespace basil { -<<<<<<< HEAD class Def; class Env; class ASTNode; @@ -393,395 +392,6 @@ namespace basil { } template<> -======= - class Def; - class Env; - class ASTNode; - class Builtin; - - u64 symbol_value(const string& symbol); - const string& symbol_for(u64 value); - - class ListValue; - class SumValue; - class IntersectValue; - class ProductValue; - class ArrayValue; - class FunctionValue; - class AliasValue; - class MacroValue; - class NamedValue; - class ModuleValue; - class DictValue; - - class Value { - const Type* _type; - union { - i64 i; - u64 u; - double f; - const Type* t; - bool b; - RC* rc; - } _data; - SourceLocation _loc; - - public: - Value(); - Value(const Type* type); - Value(i64 i, const Type* type = INT); - // Value(double d); - Value(const string& s, const Type* type = SYMBOL); - Value(const Type* type_value, const Type* type); - Value(ListValue* l); - Value(SumValue* s, const Type* type); - Value(IntersectValue* i, const Type* type); - Value(ProductValue* p); - Value(ArrayValue* a); - Value(ArrayValue* a, const Type* type); - Value(DictValue* d); - Value(ref env, const Builtin& b); - Value(FunctionValue* f, const Type* ftype); - Value(AliasValue* f); - Value(MacroValue* f); - Value(NamedValue* n, const Type* t); - Value(ModuleValue* m); - Value(ASTNode* n); - ~Value(); - Value(const Value& other); - Value& operator=(const Value& other); - - bool is_int() const; - i64 get_int() const; - i64& get_int(); - - bool is_float() const; - double get_float() const; - double& get_float(); - - bool is_symbol() const; - u64 get_symbol() const; - u64& get_symbol(); - - bool is_string() const; - const string& get_string() const; - string& get_string(); - - bool is_void() const; - - bool is_error() const; - - bool is_type() const; - const Type* get_type() const; - const Type*& get_type(); - - bool is_bool() const; - bool get_bool() const; - bool& get_bool(); - - bool is_list() const; - const ListValue& get_list() const; - ListValue& get_list(); - - bool is_array() const; - const ArrayValue& get_array() const; - ArrayValue& get_array(); - - bool is_sum() const; - const SumValue& get_sum() const; - SumValue& get_sum(); - - bool is_intersect() const; - const IntersectValue& get_intersect() const; - IntersectValue& get_intersect(); - - bool is_product() const; - const ProductValue& get_product() const; - ProductValue& get_product(); - - bool is_dict() const; - const DictValue& get_dict() const; - DictValue& get_dict(); - - bool is_function() const; - const FunctionValue& get_function() const; - FunctionValue& get_function(); - - bool is_alias() const; - const AliasValue& get_alias() const; - AliasValue& get_alias(); - - bool is_macro() const; - const MacroValue& get_macro() const; - MacroValue& get_macro(); - - bool is_runtime() const; - ASTNode* get_runtime() const; - ASTNode*& get_runtime(); - - bool is_named() const; - const NamedValue& get_named() const; - NamedValue& get_named(); - - bool is_module() const; - const ModuleValue& get_module() const; - ModuleValue& get_module(); - - const Type* type() const; - void format(stream& io) const; - u64 hash() const; - bool operator==(const Value& other) const; - bool operator!=(const Value& other) const; - Value clone() const; - - void set_location(SourceLocation loc); - SourceLocation loc() const; - }; - - class StringValue : public RC { - string _value; - - public: - StringValue(const string& value); - - string& value(); - const string& value() const; - }; - - class NamedValue : public RC { - Value _inner; - - public: - NamedValue(const Value& inner); - - Value& get(); - const Value& get() const; - }; - - class ListValue : public RC { - Value _head, _tail; - - public: - ListValue(const Value& head, const Value& tail); - - Value& head(); - const Value& head() const; - Value& tail(); - const Value& tail() const; - }; - - class SumValue : public RC { - Value _value; - - public: - SumValue(const Value& value); - - Value& value(); - const Value& value() const; - }; - - class IntersectValue : public RC { - map _values; - - public: - IntersectValue(const map& values); - - u32 size() const; - bool has(const Type* t) const; - map::const_iterator begin() const; - map::const_iterator end() const; - map::iterator begin(); - map::iterator end(); - const map& values() const; - map& values(); - }; - - class ProductValue : public RC { - vector _values; - - public: - ProductValue(const vector& values); - - u32 size() const; - Value& operator[](u32 i); - const Value& operator[](u32 i) const; - const Value* begin() const; - const Value* end() const; - Value* begin(); - Value* end(); - const vector& values() const; - }; - - class ArrayValue : public ProductValue { - public: - ArrayValue(const vector& values); - }; - - class DictValue : public RC { - map _entries; - - public: - DictValue(const map& entries); - - u32 size() const; - Value* operator[](const Value& key); - const Value* operator[](const Value& key) const; - const map::const_iterator begin() const; - const map::const_iterator end() const; - map::iterator begin(); - map::iterator end(); - const map& entries() const; - }; - - using BuiltinFn = Value (*)(ref, const Value& params); - - extern const u64 KEYWORD_ARG_BIT; - extern const u64 ARG_NAME_MASK; - - class FunctionValue : public RC { - i64 _name; - Value _code; - const Builtin* _builtin; - ref _env; - vector _args; - set* _calls; - map* _insts; - - public: - FunctionValue(ref env, const vector& args, const Value& code, i64 name = -1); - FunctionValue(ref env, const Builtin& builtin, i64 name = -1); - ~FunctionValue(); - FunctionValue(const FunctionValue& other); - FunctionValue& operator=(const FunctionValue& other); - - const vector& args() const; - const Value& body() const; - bool is_builtin() const; - u64 arity() const; - const Builtin& get_builtin() const; - ref get_env(); - const ref get_env() const; - i64 name() const; - bool found_calls() const; - bool recursive() const; - void add_call(const FunctionValue* other); - ASTNode* instantiation(const Type* args) const; - const map* instantiations() const; - void instantiate(const Type* args, ASTNode* body); - }; - - class AliasValue : public RC { - Value _value; - - public: - AliasValue(const Value& value); - - Value& value(); - const Value& value() const; - }; - - using BuiltinMacro = Value (*)(ref, const Value& params); - - class MacroValue : public RC { - Value _code; - const Builtin* _builtin; - ref _env; - vector _args; - - public: - MacroValue(ref env, const vector& args, const Value& code); - MacroValue(ref env, const Builtin& builtin); - - const vector& args() const; - const Value& body() const; - bool is_builtin() const; - u64 arity() const; - const Builtin& get_builtin() const; - ref get_env(); - const ref get_env() const; - }; - - class ModuleValue : public RC { - map _entries; - - public: - ModuleValue(const map& entries); - - const map& entries() const; - bool has(u64 name) const; - const Value& entry(u64 name) const; - }; - - Value lower(const Value& v); - - Value add(const Value& lhs, const Value& rhs); - Value sub(const Value& lhs, const Value& rhs); - Value mul(const Value& lhs, const Value& rhs); - Value div(const Value& lhs, const Value& rhs); - Value rem(const Value& lhs, const Value& rhs); - - Value logical_and(const Value& lhs, const Value& rhs); - Value logical_or(const Value& lhs, const Value& rhs); - Value logical_xor(const Value& lhs, const Value& rhs); - Value logical_not(const Value& v); - - Value equal(const Value& lhs, const Value& rhs); - Value inequal(const Value& lhs, const Value& rhs); - Value less(const Value& lhs, const Value& rhs); - Value greater(const Value& lhs, const Value& rhs); - Value less_equal(const Value& lhs, const Value& rhs); - Value greater_equal(const Value& lhs, const Value& rhs); - - Value head(const Value& v); - Value tail(const Value& v); - Value cons(const Value& head, const Value& tail); - Value empty(); - Value at(const Value& tuple, const Value& index); - vector to_vector(const Value& list); - Value is_empty(const Value& list); - - Value list_of(const Value& element); - template - Value list_of(const Value& element, Args... args) { - return cons(element, list_of(args...)); - } - Value list_of(const vector& elements); - - template - Value tuple_of(const Value& element, Args... args) { - return tuple_of(vector_of(args...)); - } - Value tuple_of(const vector& elements); - - template - Value array_of(const Value& element, Args... args) { - return array_of(vector_of(args...)); - } - Value array_of(const vector& elements); - - Value dict_of(const map& elements); - - Value error(); - - Value length(const Value& str); - - Value read_line(); - Value strcat(const Value& a, const Value& b); - Value substr(const Value& str, const Value& start, const Value& end); - - ASTNode* instantiate(SourceLocation loc, FunctionValue& fn, const Type* args_type); - Value type_of(const Value& v); - Value is(const Value& v, const Value& t); - Value cast(const Value& val, const Type* type); - Value annotate(const Value& val, const Value& type); - Value as(const Value& v, const Value& t); - Value call(ref env, Value& function, const Value& arg); - Value display(const Value& arg); - Value assign(ref env, const Value& dest, const Value& src); -} // namespace basil - -template <> ->>>>>>> f840479bc314e660171a3eb91c404f37c4be4a33 u64 hash(const basil::Value& t); void write(stream& io, const basil::Value& t);