Coder Social home page Coder Social logo

c3d / xl Goto Github PK

View Code? Open in Web Editor NEW
266.0 17.0 15.0 22.07 MB

A minimalist, general-purpose programming language based on meta-programming and parse tree rewrites

License: GNU General Public License v3.0

Shell 1.01% Makefile 0.99% C++ 62.09% C 13.16% Awk 0.04% Python 0.08% XS 19.32% Batchfile 0.15% Emacs Lisp 3.10% HTML 0.05% Pascal 0.01%
compiler compilers-design compiler-design programming-language functional-languages dialects extensible-language domain-specific-language metaprogramming homoiconic extension-language programming-languages xl-parse-tree xl-language rewrites

xl's Introduction

XL - An extensible language

WARNING: XL is a work in progress. Even if there are some bits and pieces that happen to already work, XL is presently not suitable for any serious programming. Examples given below may sometimes simply not work. Take it as a painful reminder that the work is far from finished, and, who knows, as an idea for a contribution. See HISTORY for how we came to the present mess, and Compiler status for information about what is expected to work, and Compiler overview for a quick overview of the compiler internals.

XL is an extensible programming language designed to accomodate a variety of programming needs with ease. Being extensible means that the language is designed to make it very easy for programmers to adapt the language to suit their needs, for example by adding new programming constructs. In XL, extending the language is a routine operation, much like adding a function or creating a class in more traditional programming languages.

As a validation of this bold claim, XL has a single fundamental operator, the definition operator, which you write [Pattern] is [Implementation, where] [Pattern] is a program pattern, like X+Y, and [Implementation] explains how to translate that pattern, for example Add X, Y.

Everything that is built-in in most other programming languages, from basic data types to arithmetic to conditionals to loops is provided by the standard library in XL. You can replace these constructs if you want, or add your own. Adding a new kind of loop is not more difficult in XL than adding a function, and it uses the same syntax.

For more information, please consult the XL handbook, also available in asciidoc format and PDF format

WARNING This documentation, like the compiler, is work in progress and presently extremely messy, incomplete and inaccurate.

A few simple examples

A program computing the factorial of numbers between 1 and 5 would be written as follows:

0! is 1
N! is N * (N-1)!

for I in 1..5 loop
    print "The factorial of ", I, " is ", I!

As a testament to its extensible nature, fundamental operations in XL are defined in the standard library, including operations that would be implemented using keywords in more traditional languages. For example, the if statement in XL is defined by the following code:

if [[true]]  then TrueClause else FalseClause   is TrueClause
if [[false]] then TrueClause else FalseClause   is FalseClause
if [[true]]  then TrueClause                    is TrueClause
if [[false]] then TrueClause                    is false

Similarly, the while loop is defined as follows:

while Condition loop Body is
    if Condition then
        Body
        while Condition loop Body

The standard library also provides implementations for usual operations. For example, if you evaluate 1+3, this is done through a definition for + on integer values that looks like the following (where ... denotes some implementation-dependent code):

X:integer + Y:integer is ...

Dialects and use cases

Two dialects of XL further demonstrate the extensibility of the language

  • Tao3D focuses on real-time 3D animations and can be used as a scriptable presentation software, or as someone once described it, a sort of real-time 3D LaTeX Lisp. In Tao3D, you describe a slide with a program that looks like the following code:

    import Slides
    
    slide "A simple slide example",
        * "This looks like some kind of markdown language"
        * "But code makes it powerful: your mouse is on the " & position
        position is
            if mouse_x < 0 then "left" else "right"
    

    The examples above use the new syntax in XL, with is as its definition operator. Older variants of the language used -> instead. If you downloaded a pre-built binary of Tao3D, chances are that you need to replace is with -> for the code above to work as intended.

  • ELFE, formerly ELIOT (Extensible Language for the Internet of things) was an experiment on how to write distributed software that looks like a single program, for example to control swarms of devices in the context of the Internet of Things. An example of a simple ELFE program would be:

    WORKER is "worker.mycorp.com"
    MIN_TEMP is 25
    MAX_TEMP is 55
    
    invoke WORKER,
        every 2s,
            reply
                display temperature
    
    display Temp:real is
        print "The temperature of ", WORKER, " is ", Temp
    

The present branch, bigmerge, is an ongoing effort to reconverge the various dialects of XL. At the moment, it should pass most of the ELFE-level tests, although this is not actively tested. Getting it to support Tao3D is somewhat more difficult and may take some time.

If you come from another language

If you are familiar with other programming languages, here are a few things that may surprise you about XL.

  • There are no keywords. In C, if is a keyword. In XL, it's just a name.
  • The language is designed primarily to be readable and writable by humans. For example, there are special parsing rules to match how we read the code.
  • The language is homoiconic, i.e. programs are data, like in Lisp. This forms the basis of XL extensibility.
  • Evaluation is defined entirely in terms of rewrites of a very simple abstract. syntax tree that represents the program being evaluated.
  • The precedence of all operators is dynamic, in the sense that it's loaded from a configuration file
  • The language is primarily defined by its own standard library, rather than by special rules in the compiler.

Semantics: One operator to rule them all

XL has one fundamental operator, is, the definition operator. This operator can be read as transforms into, i.e. it transforms the code that is on the left into the code that is on the right.

It can define simple variables
pi              is      3.1415926
It can define lists
words           is      "xylophage", "zygomatic", "barfitude"
It can define functions
abs X           is      if X < 0 then -X else X
It can define operators
X ≠ Y           is      not X = Y
It can define specializations for particular inputs
0!              is      1
N!  when N > 0  is      N * (N-1)!
It can define notations using arbitrary combinations of operators
A in B..C       is      A >= B and A <= C
It can define optimizations using specializations
X * 1           is      X
X + 0           is      X
It can define program structures
loop Body       is      Body; loop Body
It can define types
type complex    is      polar or cartesian
type cartesian  is      cartesian(re:number, im:number)
type polar      is      polar(mod:number, arg:number)

Note that types in XL indicate the shape of parse trees. In other words, the cartesian type above will match any parse tree that takes the shape of the word cartesian followed by two numbers, like for example cartesian(1,5).

It can define higher-order functions, i.e. functions that return functions
adder N         is      (X is N + X)
add3            is      adder 3

 // This will compute 8
 add3 5

This makes XL a truly functional language.

It can define maps associating a key to a value
my_map is
    0 is 4
    1 is 0
    8 is "World"
    27 is 32
    N when N < 45 is N + 1

// The following is "World"
my_map 8

// The following is 32
my_map[27]

// The following is 45
my_map (44)

This provides a functionality roughly equivalent to std::map in C++. However, it's really nothing more than a regular function with a number of special cases. The compiler can optimize some special kinds of mapping to provide an efficient implementation.

It can define templates (C++ terminology) or generics (Ada terminology)_
// An (inefficient) implementation of a generic 1-based array type
type array [1] of T is
    Value : T
    1 is Value
type array [N] of T when N > 1 is
    Head  : array[N-1] of T
    Tail  : T
    I when I<N is Head[I]
    I when I=N is Tail

A : array[5] of integer
for I in 1..5 loop
    A[I] := I * I
It can define variadic functions
min X       is X
min X, Y    is { Z is min Y; if X < Z then X else Z }

// Computes 4
min 7, 42, 20, 8, 4, 5, 30

In short, the single is operator covers all kinds of declarations that are found in other languages, using a single, easy to read syntax.

Syntax: Look, Ma, no keywords!

XL has no keywords. Instead, the syntax relies on a rather simple recursive descent parser.

THe parser generates a parse tree made of 8 node types. The first four node types are leaf nodes:

  • Integer is for integer numbers such as 2 or 16#FFFF_FFFF.
  • Real is for real numbers such as 2.5 or 2#1.001_001_001#e-3
  • Text is for text values such as "Hello" or 'World'. Text can be encoded using UTF-8
  • Name is for names and symbols such as ABC or **

The last four node types are inner nodes:

  • Infix are nodes where a named operator separates the operands, e.g. A+B or A and B.
  • Prefix are nodes where the operator precedes the operand, e.g. +X or sin X. By default, functions are prefix.
  • Postfix are nodes where the operator follows the operand, e.g. 3% or 5km.
  • Block are nodes with only one child surrounded by delimiters, such as (A), [A] or {A}.

Of note, the line separator is an infix that separates statements, much like the semi-colon ;. The comma , infix is traditionally used to build lists or to separate the argument of functions. Indentation forms a special kind of block.

For example, the following code:

    tell "foo",
        if A < B+C then
            hello
        world

parses as a prefix tell, with an infix , as its right argument. On the left of the , there is the text "foo". On the right, there is an indentation block with a child that is an infix line separator. On the left of the line separator is the if statement. On the right is the name world.

This parser is dynamically configurable, with the default priorities being defined by the xl.syntax file.

Parse trees are the fundamendal data structure in XL. Any data or program can be represented as a parse tree. Program evaluation is defined as transformation of parse trees.

XL as a functional language

XL can be seen as a functional language, where functions are first-class entities, i.e. you can manipulate them, pass them around, etc:

adder X:integer is (Y is Y + X)

add3 is adder 3
add5 is adder 5

print "3+2=", add3 2
print "5+17=", add5 17
print "8+2=", (adder 8) 2

However, it is a bit different in the sense that the core data structure is the parse tree. Some specific parse trees, for example A+B, are not naturally reduced to a function call, although they are subject to the same evaluation rules based on tree rewrites.

Subtlety #1: expression vs. statement

The XL parse tree is designed to represent programs in a way that is relatively natural for human beings. In that sense, it departs from languages such as Lisp or SmallTalk.

However, being readable for humans requires a few special rules to match the way we read expressions. Consider for example the following:

write sin X, cos Y

Most human beings parse this as meaning write (sin(X),cos(Y)), i.e. we call write with two values resulting from evaluating sin X and cos Y. This is not entirely logical. If write takes comma-separated arguments, why wouldn't sin also take comma-separated arguments? In other words, why doesn't this parse as write(sin(X, cos(Y))?

This shows that humans have a notion of expressions vs. statements. Expressions such as sin X have higher priority than commas and require parentheses if you want multiple arguments. By contrast, statements such as write have lower priority, and will take comma-separated argument lists. An indent or { } block begins a statement, whereas parentheses () or square brackets [] begin an expression.

There are rare cases where the default rule will not achieve the desired objective, and you will need additional parentheses.

Subtlety #2: infix vs. prefix

Another special rule is that XL will use the presence of a space on only one side of an operator to disambiguate between an infix or a prefix. For example:

write -A   // write (-A)
B - A      // (B - A)

Subtlety #3: Delayed evaluation

When you pass an argument to a function, evaluation happens only when necessary. Deferred evaluation may happen multiple times, which is necessary in many cases, but awful for performance if you do it by mistake.

Consider the following definition of every:

every Duration, Body is
    loop
        Body
        sleep Duration

In that case, we want the Body to be evaluated every iteration, since this is typically an operation that we want to execute at each loop. Is the same true for Duration? Most likely, no.

One way to force evaluation is to give a type to the argument. If you want to force early evaluation of the argument, and to check that it is a real value, you can do it as follows:

every Duration:real, Body is
    loop
        Body
        sleep Duration

Subtlelty #4: Closures

Like many functional languages, XL ensures that the value of variables is preserved for the evaluation of a given body. Consider for example:

adder X:integer is (Y is Y + X)
add3 := adder 3

In that case, adder 3 will bind X to value 3, but then the returned value outlives the scope where X was declared. However, X is referred to in the code. So the returned value is a closure which integrates the binding X is 3.

Compiler status

Work items for the XL compiler (will be turned into GitHub issues)

Language definition

Recent language changes

  • Switch from -> to is as the definition operator
  • Switch type definition from type Pattern to matching Pattern (issue #5)
  • Implement syntactic sugar (can it be lib only?), e.g. type X is Y and module X with Y or to Copy(...) is blah (issue #6)
  • Support for nested functions and proper nested scopes. See issue #8.
  • Scope injection and scoping, i.e. meaning of scope.Foo and scope Foo in the language. Deal with (scope) foo, etc. See scoping and issue #9.
  • Safe implementation of for loop using scope injection (see name parameters). This is issue #7.
  • Implement metabox ([[true]]). This is issue #10.
  • Write the interface and implementation of the type type (issue #11).
  • Implement union types (T1 or T2), as well as and and not. See issue #12
  • Revisit dynamic dispatch based on types. See issue #13
  • Implement type inheritance checks (Derived like Base). See issue #14
  • Generate lifetime values. See issue #15
  • Implement own and ref types. See issue #16
  • Implement in, out, inout types. See issue #17
  • Implement creation and destruction. See issue #18

Symbol table

  • Symbol table as an XL parse tree
  • Symbol table storing actual definitions, using is operator. Currently, the symbol table uses a "nonsensical" prefix. See issue #19

Scanner

  • Scanner
  • Scan version numbers correctly, e.g. 1.20.5 not parsed as (1.2).5
  • Better Unicode classification of letters versus punctuation. Currently, only ASCII punctuation is recognized as such.
  • Option to transparently convert -> to is (for Tao3D importing)
  • Scanner-time processing of syntax statements from imported files

Parser

  • Parser
  • "Binary" terminal node holding arbitrary binary data
  • "Literal" terminal node holding arbitrary text parsed from regexp
  • Add more error checks for failure cases, better error reporting

Renderer

  • Renderer (-show option)
  • Style renderer with -style option and .stylesheet files
  • Preserving comments in renderer output
  • Rendering binary or regexp nodes

Runtime

  • Foreign function interface (FFI) with easy macro (see native.h
  • Tag all useful runtime functions with FFI
  • Find a strategy for migrating Tao3D 1500 runtime calls
  • Cleanup the runtime from obsolete / useless functions

Interpreter (-O0)

  • Simple interpreter (-O0 or -i)
  • Explicit FFI for interpreter (extern C syntax)
  • Add "sandboxed" mode (on by default for interpreter) that disables the above
  • Connect native.h FFI to interpreter
  • Implement modules in interpreter
  • Implement error and compile_error evaluation rules in interpreter
  • Update type system to recognize matching prefix instead of type

Bytecode interpreter (-O1) BROKEN, LIKELY NOT TO BE REPAIRED

  • Bytecode interpreter (broken, do not use)
  • Fix bytecode interpreter or get rid of its remnants (-O2)
  • Redesign bytecode interpreter as emitting LLVM byte code?

LLVM "fast compiler" (-O2)

Simplistic compiler that does only run-time type analysis

  • Fast compiler functionally similar to what was used in Tao3D
  • Find strategy to re-connect it to Tao3D

LLVM optimized compiler (-O3)

  • LLVM-based compiler
  • LLVM-CRAP (adapting to multiple versions of LLVM)
  • native.h for building FFI
  • Option to emit LLVM bitcode (-B or -emit_ir)
  • Option to pass bitcode to LLVM bitcode compiler (Automatically do something like xl -B ... | llc -filetype=asm)
  • Option to directly emit disassembly
  • Pass LLVM options to LLVM (-llvm-foo)
  • Replace algo-W type analysis with forward-only type analysis.
  • Implement auto-boxing for parameter scopes
  • Self-compiling compiler
  • Rebuild Tao3D on top of "new" XL

Build system

  • Portable auto-configuring makefile (Using make-it-quick)
  • Targets for opt, debug, release and check
  • Target for install
  • Fix issue with too much stuff being rebuilt for install

CI / Testing

  • Basic QE framework / testing script (alltest script, make check)
  • Fix breakage in make check with macOS Catalina DYLD_LIBRARY_PATH
  • Basic CI in GitLab
  • Repair breakage in GitLab CI
  • Add tests for variants of LLVM in GitLab CI
  • Basic CI in GitHub

Modules

  • Reimplement module system

Standard library

  • Standard library
  • Overall structure (WIP)
  • Types
  • Arrays
  • STL-style containers
  • Algorithms
  • I/Os
  • Threading / synchronization
  • Math and numerics
  • Standard math functions
  • Complex module
  • Vectors
  • Matrices
  • Text processing
  • Time
  • Graphics (Tao3D based?)

Compiler overview

The interpreter / compiler recently went through a rather heavy merge of several incompatible branches. As a result, it inherited the best of the various branches, but is overall quite broken. There are actually several, somewhat incompatible versions of the language, some of which reside in different binaries, some in the primary binary.

The primary binary resides in the src directory. It is written in C++, and there is currently no real plan to self-compile it, although there are plans to use it as a basis for a self-compiling compiler bootstrap someday.

That primary binary contains a single scanner and parser for the XL language, but three different ways to evaluate it, which are instances of the C++ Evaluator class. These three ways to evaluate XL are selected using the -O option.

  • -O0 or -i selects an interpreter. This interpreter is essentially similar to what used to be the ELFE implementation of the language, i.e. a very small implementation that performs evaluation using parse tree rewrites. It sort of works, passes most tests with make check, and is overall sane, if a bit slow (similar to bash in my testing). It can be used for example as an extension language for your application, and does not draw much in terms of dependencies. You would add your own vocabulary using simple-to-write "modules". See the Makefile for examples. That part is the only one I can advertise as possibly useful. In particular, it correctly runs the examples in the demo directory, which are the older ELFE demos, i.e. distributed programming from a single source code.

  • -O1 selects the FastCompiler, which is basically an adaptation of the compiler used in the Tao3D program, with the intent to allow the master branch of XL to be able to support Tao3D again without major incompatibilities. It generates machine code using LLVM, but the generated code is relatively inefficient because it manipulates the parse tree. For example, an integer value is always represented by an Integer pointer, so there is always an indirection to access it. Also, while forward-porting that compiler to a version of the compiler that had dropped it, I broke a number of things. So under repair, and not currently able to support Tao3D yet.

  • -O2 and above select the Compiler class, which is an ongoing attempt at implementing XL the way I always wanted to, using type inference to generate efficient code. Presently, the type inference is so badly broken that it's likely to reject a number of very valid programs, including the basic factorial example. I have hope, though. At some point, that implementation was able to compete with C on relatively simple programs, but only with a lot of type annotations. I'm trying to achieve the same result without the type annotations. We're getting there. Like -O1, -O2 output uses LLVM to generate machine code, but that time, it's good machine code.

If you think 3 implementations is bad, wait. There is more. There is a Bytecode class that is yet another evaluator that attempted to generate a bytecode so as to accelerate interpreted evaluation, without having to bring in LLVM and all the risks associated with dynamic code generation (e.g. if you use XL as an extension language). Unfortunately, that bytecode experiment went nowhere. It's slow, ridden with bugs, and has severely damaged other parts of the compiler. I can't wait to expurge it from the compiler.

So now that's it, right? Well... No.

You see, the current XL started life as a "runtime" language for the "real" XL. The original XL looked more like Ada, and had very different type semantics. See HISTORY for all the gory details. Suffice it to say here that this compiler resides in the xl2 directory (because, yes, it was already version 2 of the language). One reason for me to keep it is that it's the only version of the compiler that ever came close to self-compiling it. So I keep it around to remind myself of various neat tricks that XL made possible, like the translate instruction.

Now, you are really done, right? Well... There's one more.

See, I really want the compiler to self-compile. So in order to prepare for that, there is a native directory where I store tidbits of what the future compiler and library would look like. Except that this is really an exploratory scratchpad, so the various modules are not even consistent with one another... But ultimately, if everything goes according to plan, the C++ compiler should be able to compile native in order to generate a compiler that would compile itself.

xl's People

Contributors

aszarsha avatar c3d avatar forissier avatar joedf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

xl's Issues

Scope injection and scoping operator

The current documentation now defines XL scoping rules in more details. Basically:

  • A map is a definition with only definitions in it, but no statement.
  • Two scoping operations exist, scope.value and scope value (often conventionally written as scope[value], although the block does not matter. The first one is called scoping and the second one is called indexing or applying. Scoping restricts the lookup while evaluating value to only what is in scope. Indexing injects the scope into the current evaluation context.

For example:

digit_spelling is
    0 is "zero"
    1 is "one"
    2 is "two"
    3 is "three"
    4 is "four"
    5 is "five"
    6 is "six"
    7 is "seven"
    8 is "eight"
    9 is "nine"

A is 3
digit_spelling.0   // "zero"
digit_spelling[0] // "zero"
digit_spelling.A  // Error: no "A" in scope
digit_spelling[A] // "three"
digit_spelling A  // "three", the block is unnecessary in that case
digit_spelling A+3 // "six" - This is a statement, so this parses as digit_spelling(A+3)
(digit_spelling A+3) // Error - This is an expression, so this parses as digit_spelling(A)+3
digit_spelling[A+3] // "six"
(digit_spelling[A+3]) // "six"

These scoping rules are partially implemented in the interpreter, see for example this test for maps, this test for anonymous maps and this test for the scoping operation. Note that all these tests mistakenly talk about "array", when it should be "map".

They are probably not working at all in the compiler at the moment.

The scanner cannot correctly scan version numbers

Currently, a version number like 1.20.3 is parsed as a a real number, 1.2 followed by an infix . operator, followed by integer value 3. This makes it indistinguishable from 1.2.3.

This could be implemented through issue #27.

Conceptual question: how is this language different than other prologs?

The main building stone on prolog systems is a "fact", same as in XL each time a new definition is given with the keyword is.
How does one differentiate from each other in conceptual level? How is the evaluator planned? AOT or lazy as in red/rebol/wolfram alpha?

I like the ideas behind XL, as I myself have as well theorized with some of them in my free time, and the whole definition of the concepts within the standard library, if well combined with literate programming can become simply game changing.

But isn't it missing a spot not looking deeper into prolog? For me it seems like implementing a more complete logic system for the definitions (concepts/facts) would definetely frame XL as a prolog descendent with a whole new set of features.

By logic programing I meant implementing a system with Horn Clauses and a facts database.

https://en.wikipedia.org/wiki/Horn_clause

Implement syntactic sugar

The documentation now describes various forms of syntactic sugar which, incidentally, make most of the code written at the time of XL2 acceptable again.

Most syntactic sugar transform a prefix notation using some particular word into an infix type annotation with possibly some additions. The words being considered include: type, class, module, function, method, procedure, to, operation, data, in, out, inout, io,
constant, variable, macro, generic, polymorphic, fast, small, global, thread and static.

The transformation are generally similar to the following, and might be implementable fully in the library:

{ type T is Impl } is { T as type is Impl }
{ type T with Interface } is { T as type with Interface }
{ type T inherits Base } is { T like Base }
{ type T like Base } is { T like Base }
{ T inherits Base } is { T like Base }
{ module M is Impl } is { M as module is Impl }
{ module M with Interface } is { M as module with Interface }  
{ procedure Proc is Impl } is { Proc as mayfail is Impl }

There are quite a few more, but the pattern is relatively general and simple.

More direct compiler analysis might be needed for a few cases, but that remains to be confirmed.

Error evaluation rules

The XL documentation now gives a description of the error type and how they are processed.

Implementing this involves:

  • Defining the error type.
  • Recognizing the error type during evaluation of statements, and propagating it out.
  • Recognizing compile_error and emit them at compile time.
  • Implementing error parameter passing, and from this, try...catch

Nubie feedback - can't build

Greetings,

I am new to XL. Looks very interesting. I am running on a modern X86-64 Linux machine. I have run into the following two issues:

  1. "configure" should be mode 777. I know it's not really used but making it mode 777 will make the system just build.
  2. When I run make, I get:

blake@i9-tower:~/Backup/xl.git$ make
Makefile:43: make-it-quick/rules.mk: No such file or directory
git submodule update --init --recursive
Submodule 'make-it-quick' (https://github.com/c3d/make-it-quick) registered for path 'make-it-quick'
Submodule 'recorder' (https://github.com/c3d/recorder) registered for path 'recorder'
Cloning into '/home/blake/Backup/xl.git/make-it-quick'...
Cloning into '/home/blake/Backup/xl.git/recorder'...
Submodule path 'make-it-quick': checked out 'df4c924ff6192c91185984132b68b578b4290a4e'
Submodule path 'recorder': checked out '563ab67c1c9733b4f2f11381602630683617ee8e'
Submodule 'make-it-quick' (https://github.com/c3d/make-it-quick) registered for path 'recorder/make-it-quick'
Cloning into '/home/blake/Backup/xl.git/recorder/make-it-quick'...
Submodule path 'recorder/make-it-quick': checked out 'df4c924ff6192c91185984132b68b578b4290a4e'
make[1]: Entering directory '/home/blake/Backup/xl.git'
[BEGIN] opt linux in [top]
make[2]: Entering directory '/home/blake/Backup/xl.git/recorder'
[CONFIG] regex NO
[BEGIN] opt linux in [top]recorder/
[CONFIG] setlinebuf OK
[CONFIG] drand48 OK
[CONFIG] sys/mman OK
[CONFIG] regex OK
[CONFIG] sigaction OK
[BEGIN] opt linux in [top]recorder/
[GENERATE] config.h
[COMPILE 1/2] recorder_ring.c
[COMPILE 2/2] recorder.c
[LINK] librecorder.so
[END] opt linux in [top]recorder/
make[2]: Leaving directory '/home/blake/Backup/xl.git/recorder'
make[2]: Entering directory '/home/blake/Backup/xl.git/src'
[CONFIG] regex NO
/bin/bash: llvm-config: command not found
[INFO] Building with LLVM version
[BEGIN] opt linux in [top]src/
[CONFIG] longlong OK
[CONFIG] ulong OK
[CONFIG] uint OK
[CONFIG] ushort OK
[CONFIG] uchar NO
[CONFIG] glob OK
[CONFIG] drand48 OK
[CONFIG] sys/socket OK
[CONFIG] sys/mman OK
[CONFIG] regex OK
[CONFIG] mingw_aligned_malloc NO
[CONFIG] posix_memalign OK
[CONFIG] sbrk OK
[CONFIG] struct_stat OK
[CONFIG] sigaction OK
/bin/bash: llvm-config: command not found
[INFO] Building with LLVM version
[BEGIN] opt linux in [top]src/
[VARIANT] lib
make[3]: Entering directory '/home/blake/Backup/xl.git/src'
/bin/bash: llvm-config: command not found
[INFO] Building with LLVM version
[BEGIN] opt linux in [top]src/[lib]
[GENERATE] basics_module.h
[GENERATE] io_module.h
[GENERATE] math_module.h
[GENERATE] text_module.h
[GENERATE] remote_module.h
[GENERATE] time_functions_module.h
[GENERATE] temperature_module.h
[GENERATE] config.h
/bin/bash: llvm-config: command not found
/bin/bash: llvm-config: command not found
/bin/bash: llvm-config: command not found
[COMPILE 1/39] compiler-expr.cpp
In file included from compiler.h:43:0,
from compiler-expr.h:40,
from compiler-expr.cpp:38:
llvm-crap.h:67:20: error: operator '<' has no left operand
#elif LLVM_VERSION < 370
^
In file included from llvm-crap.h:74:0,
from compiler.h:43,
from compiler-expr.h:40,
from compiler-expr.cpp:38:
llvm-crap.h:406:19: error: operator '>=' has no left operand

if LLVM_VERSION >= 350 && LLVM_VERSION < 360

               ^~

llvm-crap.h:414:19: error: operator '>=' has no left operand

if LLVM_VERSION >= 370 && LLVM_VERSION < 380

               ^~

llvm-crap.h:419:19: error: operator '>=' has no left operand

if LLVM_VERSION >= 400

               ^~

In file included from compiler.h:43:0,
from compiler-expr.h:40,
from compiler-expr.cpp:38:
llvm-crap.h:78:10: fatal error: llvm/IR/Type.h: No such file or directory
#include <llvm/IR/Type.h>
^~~~~~~~~~~~~~~~
compilation terminated.
../make-it-quick/rules.mk:466: recipe for target '/home/blake/Backup/xl.git/.build/linux/opt/src/compiler-expr.cpp.o' failed
make[3]: *** [/home/blake/Backup/xl.git/.build/linux/opt/src/compiler-expr.cpp.o] Error 1
make[3]: Leaving directory '/home/blake/Backup/xl.git/src'
../make-it-quick/rules.mk:365: recipe for target 'lib.variant' failed
make[2]: *** [lib.variant] Error 2
make[2]: Leaving directory '/home/blake/Backup/xl.git/src'
make-it-quick/rules.mk:362: recipe for target 'src.recurse' failed
make[1]: *** [src.recurse] Error 2
make[1]: Leaving directory '/home/blake/Backup/xl.git'

real 0m1.830s
user 0m1.470s
sys 0m0.437s
5 Errors, 0 Warnings in /home/blake/Backup/xl.git/.logs/build-linux-opt-20210725-095243.log
make-it-quick/rules.mk:204: recipe for target 'opt' failed
make: *** [opt] Error 2

Define the interface of types

Define the precise interface of a type, and how it interacts with the compiler.

There is the beginning of a very crude definition of what a type is. It needs to be fleshed out, and to interface correctly with both the interpreter and the compiler.

There will be some "meta" connections here, since type is itself a type, so I'm afraid the full definition cannot be given solely in XL without resorting to a builtin trick along the way.

Treat float literals as decimal float literals and not as binary float literals

With decimal float literals finally making it into C23 (implemented 2021 and formally adopted in 2022) and seeing XL making significant changes in its basics incl. float literals, I'd like to propose making decimal float literals the default in XL. C++ already has decimal floats as an optional extension since 2011 if I'm not mistaken.

To use binary float literals (e.g. to double the performance and unpredictably lose precision) one can easily cast a decimal float literal to a binary one. Note, it's impossible to do the other way around (cast a decimal float lit. to a binary float lit.) if we wanted to maintain the precision the user wrote the float literal with.

Implement metabox notation

Historically, XL implemented a rule that in patterns, any name that was already visible was seen as a constant. This allowed the following definition of if statements to work as long as true and false were in scope:

if true then X else Y is X
if false then X else Y is Y

The three main problems with this approach were that:

  • There was a difference between true and X, one being a constant, the other being a formal parameter, that was not obvious by reading the code and required the whole context to understand.
  • The code above would be broken if someone defined X in the same scope, since now X would become a constant instead of a formal parameter.
  • It forced the programmer to introduce named constants for expressions such as sqrt 2.

These three problems are solved in the documentation by introducing the notion of metabox. A metabox is written as [[Expr]] and evaluates to Expr in all contexts. With the metabox notation, the proper definition for if becomes:

if [[true]] then X else Y is X
if [[false]] then X else Y is Y

The previous definition will indeed make true a formal parameter.

The metabox notation can also be used in normal evaluation context in cases where evaluation of an expression must be forced. This is the case with the definition of the for loop:

for N:name in R:[range of discrete] loop Body is 
    loop_context is 
        [[N]] : R.type := R.first 
    LoopVar is loop_context.[[N]] 
    while LoopVar <= R.last loop
        (loop_context) (Body) 
        ++LoopVar 

Here, N is a name parameter, which may contain for example I when the pattern matches

for I in 1..5 loop
    print "I=", I

If loop_context was written as:

    loop_context is 
        N : R.type := R.first 

N would not be evaluated in a type annotation, but instead would create a local variable named N.

Similarly, the expression loop_context.N would not evaluate N but look it up in loop_context, where it does not exist since the variable there is called I. Using the metabox forces N to be evaluated, so that this transforms into loop_context.I, which will find I.

Metabox are a recent addition to the language are are not implemented neither in the compiler nor the interpreter.

Improve llvm-crap to deal with deprecated functions in LLVM 9.0.1

With LLVM 9.0.1, there is the following warning:

llvm-crap.cpp: In constructor ‘XL::JITPrivate::JITPrivate(int, char**)’:
llvm-crap.cpp:560:20: warning: ‘llvm::orc::LegacyRTDyldObjectLinkingLayer::LegacyRTDyldObjectLinkingLayer(llvm::orc::ExecutionSession&, llvm::orc::LegacyRTDyldObjectLinkingLayer::ResourcesGetter, llvm::orc::LegacyRTDyldObjectLinkingLayer::NotifyLoadedFtor, llvm::orc::LegacyRTDyldObjectLinkingLayer::NotifyFinalizedFtor, llvm::orc::LegacyRTDyldObjectLinkingLayer::NotifyFreedFtor)’ is deprecated [-Wdeprecated-declarations]
  560 |       moduleHandle()
      |                    ^
In file included from /usr/include/llvm/ADT/APInt.h:18,
                 from /usr/include/llvm/ADT/APFloat.h:19,
                 from /usr/include/llvm/IR/Type.h:17,
                 from llvm-crap.h:55,
                 from llvm-crap.cpp:40:
/usr/include/llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h:356:3: note: declared here
  356 |   LLVM_ATTRIBUTE_DEPRECATED(
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~
llvm-crap.cpp:560:20: warning: ‘llvm::orc::LegacyIRCompileLayer<BaseLayerT, CompileFtor>::LegacyIRCompileLayer(BaseLayerT&, CompileFtor, llvm::orc::LegacyIRCompileLayer<BaseLayerT, CompileFtor>::NotifyCompiledCallback) [with BaseLayerT = llvm::orc::LegacyRTDyldObjectLinkingLayer; CompileFtor = llvm::orc::SimpleCompiler; llvm::orc::LegacyIRCompileLayer<BaseLayerT, CompileFtor>::NotifyCompiledCallback = std::function<void(long unsigned int, std::unique_ptr<llvm::Module>)>]’ is deprecated [-Wdeprecated-declarations]
  560 |       moduleHandle()
      |                    ^
In file included from llvm-crap.cpp:168:
/usr/include/llvm/ExecutionEngine/Orc/IRCompileLayer.h:136:1: note: declared here
  136 | LegacyIRCompileLayer<BaseLayerT, CompileFtor>::LegacyIRCompileLayer(
      | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
llvm-crap.cpp:560:20: warning: ‘llvm::orc::LegacyIRTransformLayer<BaseLayerT, TransformFtor>::LegacyIRTransformLayer(BaseLayerT&, TransformFtor) [with BaseLayerT = llvm::orc::LegacyIRCompileLayer<llvm::orc::LegacyRTDyldObjectLinkingLayer, llvm::orc::SimpleCompiler>; TransformFtor = std::function<std::unique_ptr<llvm::Module>(std::unique_ptr<llvm::Module>)>]’ is deprecated [-Wdeprecated-declarations]
  560 |       moduleHandle()
      |                    ^
In file included from llvm-crap.cpp:169:
/usr/include/llvm/ExecutionEngine/Orc/IRTransformLayer.h:120:1: note: declared here
  120 | LegacyIRTransformLayer<BaseLayerT, TransformFtor>::LegacyIRTransformLayer(
      | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Fast compiler similar to what was used in Tao3D

The Tao3D compiler used a relatively straightforward compilation strategy, which has been re-imported in the compiler-fast.cpp file:

  • Keeps the parse trees in their original normal representation. For example, when you compute 2+3, you generate a parse tree for the result of the addition, which is an Integer tree with value 5.
  • The only real type-checking occurs against built-in types, by implementing a "tag test"
  • The code generated for all candidates checks if the type matches, if so keeps the computation, and the whole thing otherwise falls back to a runtime error.

This implementation is not particularly useful, except maybe in re-connecting with Tao3D (issue #32)

Implement in, out and inout parameter passing modes

The in T type optimises type T for passing as an input parameter.
The out T type optimises type T for passing as an output parameter.
The inout T type (or io T optimises type T for passing as an input/output parameter.

This relies on binding binding rules, and selecting passing by copy or by reference depending on what is more efficient.

Best way to locate shared libraries

After building and sudo make install the shared library files for xl and recorder are installed (in /usr/local/lib) but not found by the xl binary. What is the recommended way to do this? I think usually on installation the path gets embedded by r(un)path.

Switch from `type Pattern` to `matching Pattern`

The documentation used to refer to a pattern-based type definition using the type Pattern notation. For example, complex would be defined as

complex is type complex (Re:real, Im:real)

This notation is a bit confusing since it does not explain why something is a type. Also, I would like to be able to use type (Expression) to return the type of an expression (although that might be typeof)

The documentation now uses matching for that usage. With syntactic sugar, the recommended way to describe the complex type above would now be:

type complex is matching complex(Re:real, Im:real)

The interpreter and compiler should be updated to match.

Imperative flavour: continue & break in loops

Because XL is fundamentally kind of functional, it seems uneasy to simulate continue from imperative loops. Same goes for break (one could use return instead, but that'd work only without nesting I suppose which renders it completely unusable).

Any plans to provide break is builtin jmp_right_right_after_nearest_loop and continue is builtin jmp_to_the_nearest_loop_label?

Can't build native/HelloWorld

Steps:

cd xl2/samples/HelloWorld
cp ../../xl.syntax .
cp ../../native/library/runtime/C/xl.bytecode .
../../bxl HelloWorld.xl > HelloWorld_native.cpp
g++ -I../../ HelloWorld_native.cpp -o HelloWorld_native

gives:

/usr/bin/ld: /tmp/ccIKhQRM.o: in function `main':
HelloWorld_native.cpp:(.text+0x112): undefined reference to `xl::ui::console::arguments[abi:cxx11]'
collect2: error: ld returned 1 exit status

Is this supposed to work now? Or is more work needed?

Foreign function interface (FFI) with easy to use macro

Provide an easy-to-use way to generate the foreign function interface (FFI) to call into C code.

The objective is make it very quick to "export" a C function in such a way that it can be called by XL code. Ideally, the whole process would be automated by the C++ compiler using meta-programming techniques.

Add to https://github.com/kostya/benchmarks/

I'm fully aware XL undergoes a big "merging" of all the diverse efforts emerging throughout the last 10-15 years and that it also has some sharp edges.

It's though already very capable and it definitely makes sense to keep an eye also on the performance side (both speed of debug build compilation /assuming unoptimized debug build by default for subsecond compilation and production build upon explicit request/ and the running speed of the resulting binary).

Currently the most curated and maintained "generic" benchmark is https://github.com/kostya/benchmarks/ . It'd be great to see XL there as well (the production build of course).

[Comment/Suggestion] The Definition Operator

I couldn't find much background about the considerations underpinning the earlier decision to swap out -> for is as the definition operator, which will potentially be very relevant in what I am aware might sound like I'm framing to be a simple Find/Replace... job. But, for now, I'm providing the following comments within the context of an ideal world:

Comment

The definition operator ought not to be is

Justification

1. It is not easy-to-read

As a single entity and a word in the English language, "is" is undeniably easy-to-read for a lot of people. Placed within lines or paragraphs of code, a lot of which will be comprised of other latin-based strings, it suffers from being paradoxically too easy-to-read (and instinctively not processed during a skim-read), thus making it more difficult to read quickly and efficiently, and forcing one to engage more cognitively to dissociate from linguistic context to a non-linguistic context but one in which it's intentionally mimicking a linguistic approximation in a jarring way. Linguistics aside, as initially alluded to, it is easy to lose among a group of other strings that camouflage its occurrence.

2. It's a misappropriation

It is the singularly-defined syntactic form for an operator that takes on the appearance of an English word in a language that, otherwise, has no other hard-coded entities we would be required to invoke that parallel a meaningful relationship between a coding language one would otherwise be free to cultivate esoterically dissociated syntax divorced from a spurious linguistic connotation hinted at by such a specific choice of entity. Somewhat frustratingly, it uses that word which, in English contextual prose, seldomly crops up in its copulative (relational) state: "is" is most often used intransitively, which underpins the previous argument; here, it copulates one clause to bond it relationally to another, which is, indeed, what would be valid grammatically when speaking, most typically in mathematical (and computer science) discussions as a loose-and-free way to infer equality, but never in formal usage. The overall effect is one that has borrowed a well-recognised object commonly used in one way, to crowbar it into an unrelated situation where its less-common form serves a proxy for a muddy-sounding description of what purpose it is intended to serve.

3. It's language-biased

"is" is specifically taken from English. Aside from those on the team likely having English as a common language between them, there's no need or reason to impose that on those who'd not want that. Other programming languages do this, rightly-or-wrongly, because of the industry that is driven by English-language developers, necessitating anyone wishing to develop professional basically have to deal with that. But that's part of what what XL appears to free itself from the burden of formally pre-defined syntax of procedural code where things have names and names ended up English-like. However, imagine how really weird it would seem to have a programming language where everything semantically or syntactically was curated by the coder, except for the one appearance of the Mandarin word (wèi). We'd live, and I would probably use it to define a synonym to serve as a definition operator, if that is possible (and, if so, it still undesirable).

Suggested Solution

Use a symbol that is representative, without being language-specific. It might be additional effort to implement an accessible way to type special symbols from selected unicode blocks, but that could be a worthwhile endeavour in the long-run for a language untethered from what one conventionally sees on their keyboard's keys.

  1. The most obvious would be to revert back to the symbolic arrow ->.

  2. To be distinct, whilst remaining familiar, another unicode arrow form could be used. Given the nature of the operator is effectively (in mathematical terms) a functional mapping of syntax onto an operation, the most representatively authentic arrow form might be (the maths symbol for maps onto, e.g. 𝑓﹕𝑧 ↦ 𝑧² + 𝑐) but doesn't print well in some fonts.

    Other symbol suggestions:

    • ("implies"), or the keyboard-friendly => which we all know from JS
    • (a familiar definition operator used in other languages) or the easier-to-type :=
    • (no idea)
    • a symbolic, decorative colon
    • ("is equivalent to", but would prevent it being defined for use as a mathematical operator)
    • an arrow that prints consistently well, which I have on shortcut
  3. Create your own symbol and stick it in a reserved block of the unicode table the compiler can access.

Rework the symbol table to store actual definitions

Currently, the symbol table in the compiler is implemented as an XL prefix, renamed as Scope, containing definitions that are stored as infix.

The addition of a symbol is done by inserting them in a somewhat nonsensical name for a rewrite and its children. This means that printing a symbol table as a regular tree gives a somewhat unexpected result.

(lldb) xl symbols
nil tell host:text, code:tree as integer is builtin tell
( invoke host:text, code:tree as tree is builtin invoke
nil; reply code:tree as integer is builtin reply
( listen_forking as integer is builtin listen_forking
nil; nil); listen_hook hook:tree as tree is builtin listen_hook
( listen_received is listen_received
nil; nil); nil); ask host:text, code:tree as tree is builtin ask
( listen_on port:integer as integer is builtin listen_on
nil; nil); listen as integer is builtin listen
nil; nil

This is for an entry that really contains the following definitions:

xl symbols.pointer
tell host:text, code:tree as integer is builtin tell
invoke host:text, code:tree as tree is builtin invoke
reply code:tree as integer is builtin reply
listen_forking as integer is builtin listen_forking
listen_hook hook:tree as tree is builtin listen_hook
listen_received is listen_received
ask host:text, code:tree as tree is builtin ask
listen_on port:integer as integer is builtin listen_on
listen as integer is builtin listen

The lookup is O(log(N)) by selecting which branch to follow using a tree hash.

I believe that it's possible to store the entries without the nil, simply balancing between left and right as you need.

Generate lifetime values

Lifetime is a new concept in XL, inspired by the Rust "borrow checker", and destined to make it possible in XL to implement a wider class of borrow-checks from the library.

A lifetime value is generated for the expression lifetime X, which returns a compile-time constant that can be compared to other lifetime constants. For instance, a reference type can be made from an owning value with the following condition:

type ref T
type own T

Source:own T as Target:ref T when lifetime Target < lifetime Source is ...
Source:own T as Target:ref T when not (lifetime Target < lifetime Source) is
    compile_error "The reference %1 might outlive the source %2", Target, Source

Implement for loops using scope injection

The current for loop definition is broken with recent evolutions of the language. This is the root cause for issue #4 and many other similar failures. The bottom line is that in the current implementation, the loop variable is unusable.

There was no way to properly inject a name in the body scope, so the loop was really "hacked" into the compiler in the historical Tao3D compiler, and did a number of things that are not as easy to do with the optimizing compiler, like having C++ code modify the values of variables directly.

The correct definition is now described in the documentation:

for N:name in R:[range of discrete] loop Body is 
    loop_context is 
        [[N]] : R.type := R.first 
    LoopVar is loop_context.[[N]] 
    while LoopVar <= R.last loop
        (loop_context) (Body) 
        ++LoopVar 

It uses a number of things that are not yet implemented and may need further clarification:

  • The N:name notation to match a name in the parse tree should work even with the new type system, but was only tested in the old one.
  • The [range of discrete] notation uses the range and discrete types, neither of which is presently implemented.
  • The creation of a context like loop_context is only partially supported, and may need rework of the symbol table to fulfil its potential.
  • The notation LoopVar is loop_context.[[N]] remains to be checked for feasibility. This usage of such an alias declaration would imply to be able to write into the alias. But so far, a lot of the documentation assumes that is can generally be implemented by generating a function. It's unclear if allowing an is definition to be used to write by default is a good idea. Maybe variable LoopVar is loop_context.[[N]] would be useful here.
  • The (loop_context)(Body) notation injects a scope into Body, and is used to clarify that we are not looking for a loop_context prefix, but for a scope. Things should work without parenthese. Such use of declaration-only bodies as maps is documented but not implemented yet.
  • The ++LoopVar notation uses a general increment operator for all discrete types that is not implemented yet.

NULL/nil/none/void/... value considered harmful (a "billion dollar mistake" of Tony Hoare)

This post will be written with XL being conceptual language in mind. At some places it might sound differently, but I've chosen to use that terminology to be clear about the intent. The concepts I'm presenting are though generic and apply as well to XL as a conceptual language.

I believe everybody is familiar with the "billion dollar mistake" (as Tony Hoare calls it). I saw there is a bunch of recent commits (c3dde14 , 68b258d , 3da52b4 , 11ed0bd ) leveraging nil functionality.

I'd like to oppose using nil value. The question is though "how to deal with such interfaces"?

My answer is "use ephemeral optionals". First "optionals" is a well known concept (Haskell's Maybe, Rust's Option, V's ?, Zig's ...). Despite all the pros (being dead-simple to reason about even in highly parallel systems, no stack unwinding like with exceptions, universal - can be used for errors or alternative values of any kind etc.), it brings some serious usability issues.

Namely it "reifies" the fact whether it's a value or error and thus feels like a "primed bomb" you're tossing around. So what does the ephemeral mean? That means just one thing - the primed bomb can't be assigned to anything. In other words it can be only returned and directly at the caller site has to be dealt with.

Sounds still too inflexible? Well, not anymore if you provide some sugar covering the most common use case - namely propagate (return) the optional as I don't want to handle it here yet. V lang does this perfectly - you just need to write ? after a function call and it'll propagate it. And to handle the ephemeral optional you just append or { print( err ) } to the function call to "catch" the optional and work with the data the callee has put into the optional (which is under the hoods a struct holding arbitrary data).

All in all I'd like XL to get rid of a "lonely" nil value completely and instead use this ephemeral optionals trick together with e.g. sum types (aka tagged unions aka variants). Note there should definitely exist a nil type (a type can't be assigned to anything in run time because it's a type 😉 and thus doesn't appear in run time) - e.g. for tree structures - but no lonely nil value. So a function returning sum type T | nil (the value is either of type T or of type nil - note here nil is not lonely but "bound" to T) will be something totally different than ?T (aka "optional T" - the value is either of type T or an ephemeral error).

Note one can easily have ?(T | nil). E.g. a function which shall return the leftmost child of the leftmost tree node at level 5 - if the tree depth is 4 or less, it'll return an error otherwise it'll return the value of type "sum type of T and nil" which can be assigned to a variable unlike a lonely nil (this assumes it's impossible to define a sum type with less than two types - otherwise one could again create the generic nil bomb by defining a sumtype consisting of only nil type). In other words nil as value actually exists under the hood, but always as a "special" case of some more important value thus providing a compile time explicit guarantee that all such nil values under the hood will be handled and will not leak anywhere.

A bit related is the fact, that nil value should never be used to designate an all-encompassing "alternative value". Not having nil value at all (but only a nil type) is a simple measure to ensure this. Thus e.g. all the I/O functions in one of the commits I refer to above should return some optional instead of nil.

Of course this ephemeral optionals mechanism is totally agnostic from any exception-like or effect-like or Dylan-like or any other mechanism for dealing with alternative computational branches. So no need to do anything on that side.

Any thoughts on this?

Revisit dynamic dispatch based on types

Dynamic dispatch is currently somewhat implemented both in the compiler and interpreter.

However, given recent evolutions of the language, notably with respect to pattern matching, this code needs to be seriously revisited.

Key changes:

  • Support for type inheritance / implicit conversions (two passes)
  • Support for metaboxes (issue #10) in patterns
  • Smarter dispatch in maps for common cases, e.g. contiguous values (ideally, turn that to "switch" statements in LLVM IR and let the switch optimizer do its magic).

The factorial example in the README does not work as expected

The README shows the following code for a program that compute the factorial of 1 to 5:

0! is 1
N! is N * (N-1)!

for I in 1..5 loop
    print "The factorial of ", I, " is ", I!

I expect this to print:

The factorial of 1 is 1
The factorial of 2 is 2
The factorial of 3 is 6
The factorial of 4 is 24
The factorial of 5 is 120

However, the following is printed instead:

The factorial of I is N * (N - 1)!
The factorial of I is N * (N - 1)!
The factorial of I is N * (N - 1)!
The factorial of I is N * (N - 1)!
false

Environment: gcc:latest Docker image (Debian)
Occurs in both interpreted (xl -i file.xl) and compiled (xl file.xl) forms.

Highlight.js colorization updates

There is basic support for colorization of Tao3D-style XL in Highlight.js.

The recent language updates have not been incorporated into this file, notably:

  • is as the rewrite operator
  • procedure, to, function, type, etc. as syntactic sugar keywords
  • The XL modules are not known. But should we colorize the modules based on their name, or rather on the fact that they follow an import or use?
  • Defining what we colorize as "keywords", what we colorize as "modules", etc.

Binary node in the parse tree

It might be interesting to have a binary tree to hold arbitrary (possibly typed) binary data in the parse tree. This could also be represented as a very large integer, which could use the base-16 or base-64 notation.

Example:

some_data is bits 16#FFFF_FFFE_FFFD_FFFC_FFFB__FFFA_FFF9_FFF8_FFF7_FFF6_FFF5_FFF4_FFF3_FFF2_FFF1_FFF0

Currently, the above does not work because the integer value is too big. There is probably a need for specific prefixes (e.g. bits above), which could also serve as an elementary type for the value.

This could be implemented through issue #27.

Emacs colorization updates

There is a very old xl2/xl.el Emacs Lisp file to offer some colorization, indentation, etc.

It's totally outdated and is badly in need of some repair.

Chat room?

Any plans for a chat room?

I am trying to read through and test things from the large docs, but sometimes there is just some niggling details that would be nice to bounce off other people.

Options:

  • IRC
  • Gitter
  • matrix

Basic Examples Compiler Explanation

Maybe if you have time you could explain how the compiler works (or is supposed to work), specifically for the two basic examples of factorial (or fibonacci) and hello world in bxl (which, if I understand correctly is abandoned because of typing issues) and your newer fast branch?

Add nodes with arbitrary syntax using a regexp

The language could be made more extensible if you could add arbitrary terminal node types defined for example by a regular expression. This could be used to address issue #20 or issue #26.

In the syntax, it could be something like:

TEXT
        "<<" ">>"
        "HTML" "END_HTML"

CUSTOM
        version_number "[0-9]+\(\.[0-9]+\)+"
        bits "bits 16#[0-9a-fA-F_]+"

Note that the renderer should be updated to correctly render this kind of node. Also think about colorization (issue #23 and issue #24).

Implement nested scopes

The documentation now describes scoping rules which are currently, for the most part, not implemented.

The Tao3D compiler was notoriously bad at scoping, sometimes leaking definitions in the enclosing scope in order to workaround limitations with the LLVM code generation used at the time. Nesting should now be possible, although some special care must be taken with respect to how variables from the enclosing context are accessed by the inner context. I believe that LLVM should be supporting that since the dialect of C / C++ that GNU support does support nested functions, but I have not studied yet how this is represented at the LLVM IR level yet.

Symbol lookup is presently relatively well scoped, but additional testing is needed. Nested scope might even be working correctly in the interpreter.

How to build on Ubuntu 18.04.1?

I have simply cloned this repo, did cd xl/xl2 and then make:

make[1]: Entering directory '/home/pp/git/xl/xl2'
Compiling main.cpp
Generating dependencies in obj/linux/debug/Makefile.depend
Compiling main.cpp
Compiling main.cpp
Compiling main.cpp
make[2]: Entering directory '/home/pp/git/xl/xl2'
Compiling predepend
Compiling predepend
make[2]: Leaving directory '/home/pp/git/xl/xl2'
----------- Making debug in /home/pp/git/xl/xl2 ------------
Dependencies for debug done
Compiling main.cpp
Compiling scanner.cpp
Compiling tree.cpp
Compiling context.cpp
Compiling parser.cpp
Compiling ctrans.cpp
Compiling options.cpp
Compiling errors.cpp
Building xl
cd bootstrap && make test
make[2]: Entering directory '/home/pp/git/xl/xl2/bootstrap'
Compiling compiler.xl
Generating dependencies in ../obj/linux/debug/bootstrap/Makefile.depend
Compiling compiler.xl
Compiling compiler.xl
Compiling compiler.xl
make[3]: Entering directory '/home/pp/git/xl/xl2/bootstrap'
No dependency for %
Compiling predepend
Compiling predepend
make[3]: Leaving directory '/home/pp/git/xl/xl2/bootstrap'
make[3]: Entering directory '/home/pp/git/xl/xl2/bootstrap'
----------- Making debug in /home/pp/git/xl/xl2/bootstrap ------------
Dependencies for debug done
cp ../xl_lib.h .
cp ../xl.syntax .
Compiling compiler.xl
Compiling ../obj/linux/debug/bootstrap/compiler.xl.C
../obj/linux/debug/bootstrap/compiler.xl.C: In function ‘void xl::translator::xl2cargs(xl::parser::tree::tree)’:
../obj/linux/debug/bootstrap/compiler.xl.C:2046:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
../obj/linux/debug/bootstrap/compiler.xl.C: In function ‘void xl::translator::xl2c(xl::parser::tree::tree)’:
../obj/linux/debug/bootstrap/compiler.xl.C:2658:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
../obj/linux/debug/bootstrap/compiler.xl.C:3492:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
../obj/linux/debug/bootstrap/compiler.xl.C: In function ‘bool xl::translator::xlnamespacescope(xl::parser::tree::tree)’:
../obj/linux/debug/bootstrap/compiler.xl.C:3822:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
Building bxl
---------- Done with debug in /home/pp/git/xl/xl2/bootstrap ----------
make[3]: Leaving directory '/home/pp/git/xl/xl2/bootstrap'
make PRODUCT=bbxl XL=./bxl OBJROOT="../obj/linux/debugbbxl"
make[3]: Entering directory '/home/pp/git/xl/xl2/bootstrap'
Compiling compiler.xl
Generating dependencies in ../obj/linux/debugbbxl/bootstrap/Makefile.depend
Compiling compiler.xl
Compiling compiler.xl
Compiling compiler.xl
make[4]: Entering directory '/home/pp/git/xl/xl2/bootstrap'
No dependency for %
Compiling predepend
Compiling predepend
make[4]: Leaving directory '/home/pp/git/xl/xl2/bootstrap'
make[4]: Entering directory '/home/pp/git/xl/xl2/bootstrap'
----------- Making debug in /home/pp/git/xl/xl2/bootstrap ------------
Dependencies for debug done
Compiling compiler.xl
Compiling ../obj/linux/debugbbxl/bootstrap/compiler.xl.C
../obj/linux/debugbbxl/bootstrap/compiler.xl.C: In function ‘void xl::translator::xl2cargs(xl::parser::tree::tree)’:
../obj/linux/debugbbxl/bootstrap/compiler.xl.C:3608:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
../obj/linux/debugbbxl/bootstrap/compiler.xl.C: In function ‘void xl::translator::xl2c(xl::parser::tree::tree)’:
../obj/linux/debugbbxl/bootstrap/compiler.xl.C:4304:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
../obj/linux/debugbbxl/bootstrap/compiler.xl.C:5246:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
../obj/linux/debugbbxl/bootstrap/compiler.xl.C: In function ‘bool xl::translator::xlnamespacescope(xl::parser::tree::tree)’:
../obj/linux/debugbbxl/bootstrap/compiler.xl.C:5744:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
Building bbxl
---------- Done with debug in /home/pp/git/xl/xl2/bootstrap ----------
make[4]: Leaving directory '/home/pp/git/xl/xl2/bootstrap'
make[3]: Leaving directory '/home/pp/git/xl/xl2/bootstrap'
make PRODUCT=bbbxl XL=./bbxl OBJROOT="../obj/linux/debugbbxl_test"
make[3]: Entering directory '/home/pp/git/xl/xl2/bootstrap'
Compiling compiler.xl
Generating dependencies in ../obj/linux/debugbbxl_test/bootstrap/Makefile.depend
Compiling compiler.xl
Compiling compiler.xl
Compiling compiler.xl
make[4]: Entering directory '/home/pp/git/xl/xl2/bootstrap'
No dependency for %
Compiling predepend
Compiling predepend
make[4]: Leaving directory '/home/pp/git/xl/xl2/bootstrap'
make[4]: Entering directory '/home/pp/git/xl/xl2/bootstrap'
----------- Making debug in /home/pp/git/xl/xl2/bootstrap ------------
Dependencies for debug done
Compiling compiler.xl
Compiling ../obj/linux/debugbbxl_test/bootstrap/compiler.xl.C
../obj/linux/debugbbxl_test/bootstrap/compiler.xl.C: In function ‘void xl::translator::xl2cargs(xl::parser::tree::tree)’:
../obj/linux/debugbbxl_test/bootstrap/compiler.xl.C:3608:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
../obj/linux/debugbbxl_test/bootstrap/compiler.xl.C: In function ‘void xl::translator::xl2c(xl::parser::tree::tree)’:
../obj/linux/debugbbxl_test/bootstrap/compiler.xl.C:4304:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
../obj/linux/debugbbxl_test/bootstrap/compiler.xl.C:5246:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
../obj/linux/debugbbxl_test/bootstrap/compiler.xl.C: In function ‘bool xl::translator::xlnamespacescope(xl::parser::tree::tree)’:
../obj/linux/debugbbxl_test/bootstrap/compiler.xl.C:5744:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
Building bbbxl
---------- Done with debug in /home/pp/git/xl/xl2/bootstrap ----------
make[4]: Leaving directory '/home/pp/git/xl/xl2/bootstrap'
make[3]: Leaving directory '/home/pp/git/xl/xl2/bootstrap'
make PRODUCT=bbbbxl XL=./bbbxl OBJROOT="../obj/linux/debugbbxl_stability"
make[3]: Entering directory '/home/pp/git/xl/xl2/bootstrap'
Compiling compiler.xl
Generating dependencies in ../obj/linux/debugbbxl_stability/bootstrap/Makefile.depend
Compiling compiler.xl
Compiling compiler.xl
Compiling compiler.xl
make[4]: Entering directory '/home/pp/git/xl/xl2/bootstrap'
No dependency for %
Compiling predepend
Compiling predepend
make[4]: Leaving directory '/home/pp/git/xl/xl2/bootstrap'
make[4]: Entering directory '/home/pp/git/xl/xl2/bootstrap'
----------- Making debug in /home/pp/git/xl/xl2/bootstrap ------------
Dependencies for debug done
Compiling compiler.xl
Compiling ../obj/linux/debugbbxl_stability/bootstrap/compiler.xl.C
../obj/linux/debugbbxl_stability/bootstrap/compiler.xl.C: In function ‘void xl::translator::xl2cargs(xl::parser::tree::tree)’:
../obj/linux/debugbbxl_stability/bootstrap/compiler.xl.C:3608:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
../obj/linux/debugbbxl_stability/bootstrap/compiler.xl.C: In function ‘void xl::translator::xl2c(xl::parser::tree::tree)’:
../obj/linux/debugbbxl_stability/bootstrap/compiler.xl.C:4304:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
../obj/linux/debugbbxl_stability/bootstrap/compiler.xl.C:5246:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
../obj/linux/debugbbxl_stability/bootstrap/compiler.xl.C: In function ‘bool xl::translator::xlnamespacescope(xl::parser::tree::tree)’:
../obj/linux/debugbbxl_stability/bootstrap/compiler.xl.C:5744:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
Building bbbbxl
---------- Done with debug in /home/pp/git/xl/xl2/bootstrap ----------
make[4]: Leaving directory '/home/pp/git/xl/xl2/bootstrap'
make[3]: Leaving directory '/home/pp/git/xl/xl2/bootstrap'
echo ------ Differences: -----------------------------------------
------ Differences: -----------------------------------------
diff ../obj/linux/debugbbxl_test/bootstrap/compiler.xl.C \
     ../obj/linux/debugbbxl_stability/bootstrap/compiler.xl.C
echo ------ End of Differences -----------------------------------
------ End of Differences -----------------------------------
make[2]: Leaving directory '/home/pp/git/xl/xl2/bootstrap'
cd native && make test
make[2]: Entering directory '/home/pp/git/xl/xl2/native'
Compiling compiler.xl
Generating dependencies in ../obj/linux/debug/native/Makefile.depend
Compiling compiler.xl
Compiling compiler.xl
Compiling compiler.xl
make[3]: Entering directory '/home/pp/git/xl/xl2/native'
No dependency for %
Compiling predepend
Compiling predepend
make[3]: Leaving directory '/home/pp/git/xl/xl2/native'
make[3]: Entering directory '/home/pp/git/xl/xl2/native'
----------- Making debug in /home/pp/git/xl/xl2/native ------------
Dependencies for debug done
cp ../xl_lib.h .
cp ../xl.syntax .
Compiling compiler.xl
Compiling ../obj/linux/debug/native/compiler.xl.C
../obj/linux/debug/native/compiler.xl.C: In function ‘void xl::codegenerator::xl2cargs(xl::bytecode::bytecode)’:
../obj/linux/debug/native/compiler.xl.C:4295:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
../obj/linux/debug/native/compiler.xl.C: In function ‘void xl::codegenerator::xl2c(xl::bytecode::bytecode)’:
../obj/linux/debug/native/compiler.xl.C:6033:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
../obj/linux/debug/native/compiler.xl.C: In function ‘bool xl::codegenerator::xlnamespacescope(xl::bytecode::bytecode)’:
../obj/linux/debug/native/compiler.xl.C:6640:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
../obj/linux/debug/native/compiler.xl.C: In function ‘xl::parser::tree::treenode* xl::errors::errortree(xl::parser::tree::tree)’:
../obj/linux/debug/native/compiler.xl.C:9008:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
../obj/linux/debug/native/compiler.xl.C: In function ‘xl::parser::tree::treenode* xl::plugin::listing::translator_220(xl::parser::tree::tree, xl::parser::tree::tree, xl::symbols::rewrite, xl::parser::tree::treemap&)’:
../obj/linux/debug/native/compiler.xl.C:13427:6: warning: variable ‘XLtranslateDone’ set but not used [-Wunused-but-set-variable]
 bool XLtranslateDone = false;
      ^~~~~~~~~~~~~~~
../obj/linux/debug/native/compiler.xl.C: In function ‘xl::parser::tree::treenode* xl::plugin::xl2c::convert(xl::parser::tree::tree)’:
../obj/linux/debug/native/compiler.xl.C:13654:33: error: cannot convert ‘bool’ to ‘xl::parser::tree::tree {aka xl::parser::tree::treenode*}’ in initialization
 xl::parser::tree::tree fnargs = false;
                                 ^~~~~
../obj/linux/debug/native/compiler.xl.C: In function ‘xl::parser::tree::treenode* xl::semantics::declarations::enterdeclaration(xl::parser::tree::tree, xl::parser::tree::tree, xl::parser::tree::tree, bool)’:
../obj/linux/debug/native/compiler.xl.C:17313:31: error: expected primary-expression before ‘decltype’
 xl::semantics::types::anytype decltype = xl::semantics::types::evaluatetype (type);
                               ^~~~~~~~
../obj/linux/debug/native/compiler.xl.C:17314:39: error: expected primary-expression before ‘decltype’
 if (xl::semantics::types::istypetype (decltype)) {
                                       ^~~~~~~~
../obj/linux/debug/native/compiler.xl.C:17321:83: error: expected primary-expression before ‘decltype’
 ::types::anytype declbasetype = xl::semantics::types::nonsourcetype (decltype);
                                                                      ^~~~~~~~
../obj/linux/debug/native/compiler.xl.C:17336:58: error: expected primary-expression before ‘decltype’
 (result = xl::semantics::iterators::enteriterator (name, decltype, value, name));
                                                          ^~~~~~~~
../obj/linux/debug/native/compiler.xl.C:17341:58: error: expected primary-expression before ‘decltype’
 (result = xl::semantics::functions::enterfunction (name, decltype, value, name));
                                                          ^~~~~~~~
../obj/linux/debug/native/compiler.xl.C:17361:2: error: expected primary-expression before ‘decltype’
 (decltype = xl::semantics::types::evaluatetype (type));
  ^~~~~~~~
../obj/linux/debug/native/compiler.xl.C:17361:2: error: expected ‘)’ before ‘decltype’
../obj/linux/debug/native/compiler.xl.C:17362:115: error: expected primary-expression before ‘decltype’
 type2 = dynamic_cast< xl::semantics::types::generics::generictype > (decltype);
                                                                      ^~~~~~~~
../obj/linux/debug/native/compiler.xl.C:17362:115: error: expected ‘)’ before ‘decltype’
In file included from /usr/include/c++/7/cassert:44:0,
                 from ./xl_lib.h:39,
                 from ../obj/linux/debug/native/compiler.xl.C:2:
../obj/linux/debug/native/compiler.xl.C: In function ‘bool xl::semantics::generics::instantiatedtypematch(xl::semantics::types::anytype, xl::semantics::types::anytype)’:
../obj/linux/debug/native/compiler.xl.C:23137:32: warning: the address of ‘bool xl::semantics::generics::instantiatedtypematch(xl::semantics::types::anytype, xl::semantics::types::anytype)’ will never be NULL [-Waddress]
 assert ((instantiatedtypematch == 0));
          ~~~~~~~~~~~~~~~~~~~~~~^~~~
Makefile:138: recipe for target '../obj/linux/debug/native/compiler.xl.o' failed
make[3]: *** [../obj/linux/debug/native/compiler.xl.o] Error 1
make[3]: Leaving directory '/home/pp/git/xl/xl2/native'
../Makefile.rules:62: recipe for target 'debug' failed
make[2]: *** [debug] Error 2
make[2]: Leaving directory '/home/pp/git/xl/xl2/native'
Makefile:49: recipe for target 'native_compiler' failed
make[1]: *** [native_compiler] Error 2
make[1]: Leaving directory '/home/pp/git/xl/xl2'
Makefile.rules:62: recipe for target 'debug' failed
make: *** [debug] Error 2

What can I do to build xl2 and xlr? I am very keen to try it out.

Better classification of Unicode characters

The current implementation reads its input in Unicode UTF-8 format, and makes crude attempts at accepting Unicode.

This was good enough for Tao3D to deal with multi-lingual text, including in languages such as Hebrew or Arabic. However, that implementation is a bit naive with respect to distinguishing Unicode letters from non-letter characters.

For example, 𝝿_2 or étalon are valid XL names, and this is intentional, but ⇒A2 is presently a valid XL name, and this can easily be considered a bug.

Connect the interpreter to the foreign-function interface mechanism

Issue #29 documents a foreign-function interface, but that currently only works with the compiler.

The same mechanism should also export functions for use by the interpreter, i.e. the evaluate function should be visible from the interpreter just because it's been tagged as XL_NATIVE.

Implement type expressions

Implement type expressions as documented.

The most important types are:

  • T1 or T2, e.g. for T or error (aka mayfail T)
  • T1 and T2, mostly for traits, e.g. number and ordered
  • not T, mostly for rules that exclude a given set of type, e.g. ln X:not positive is error "Log of negative"

Internally, the compiler currently generates very basic union types using the | operator.

Ideally, we would be able to implement all this in the library only, once the type interface (issue #11) is properly defined. For example:

type T1 or T2 is
    contains Value is T1.contains Value or T2.contains Value

Process `syntax` statements during scanning (in `import` / `use`)

A syntax statement within an imported file will not currently change the syntax in the importing file, only locally.

In order for the syntax extensions to propagate to the importing file, we need the scanner and parser to recognize that there is a syntax statement, mark it so that import will be aware of it. That, in turn, may require import to be processed early, e.g. during scanning.

Example:

// imported.xl
module IMPORTED with
    syntax { POSTFIX 130 cm mm km dm m }
    type distance is matching (D:real m)
    D:real km as distance is (D * 1000) m
    D:real mm as distance is (D * 0.001) m

// Importing file
use IMPORTED

D : distance := 3.2km

Implement modules

XL modules have existed in two implementations so far:

  • The XL2 implementation which is hierarchical, but has no dynamic loading, no version numbers nor much self-desccription / introspection.
  • The Tao3D implementation has ABI and version checking, can load modules dynamically, has support for module description, but is not hierarchical and, paradoxically, does not deal too well with static modules.

Neither implementation covers syntax statements in modules correctly.

The new implementation should be:

  • Hierarchical
  • Cover static modules well
  • Support dynamic modules
  • Support version checks, ideally using semantic versioning (which depends on issue #20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.