Giter Club home page Giter Club logo

urn's Introduction

Urn Travis Build Status Build status

Urn is a new language developed by SquidDev, and demhydraz. Urn is a Lisp dialect with a focus on minimalism which compiles to Lua.

What?

  • A minimal¹ Lisp implementation, with full support for compile time code execution and macros.
  • Support for Lua 5.1, 5.2 and 5.3. Should also work with LuaJIT.
  • Lisp-1 scoping rules (functions and data share the same namespace).
  • Influenced by a whole range of Lisp implementations, including Common Lisp and Clojure.
  • Produces standalone, optimised Lua files: no dependencies on a standard library.

¹: Minimalism is an implementation detail.

Features

Pattern matching

> (case '("x" (foo 2 3))
.   [(string?  @ ?x) (.. "Got a string " x)]
.   [("x" (foo . ?x)) (.. "Got some remaining values " (pretty x))])
out = "Got some remaining values (2 3)"

Various looping constructs

> (loop [(o '())
.        (l '(1 2 3))]
.   [(empty? l) o]
.   (recur (cons (car l) o) (cdr l)))
out = (3 2 1)

Powerful assertion and testing framework

> (import test ())
out = nil
> (affirm (eq? '("foo" "bar" "")
.              (string/split "foo-bar" "-")))
[ERROR] <stdin>:1 (compile#111{split,temp}:46): Assertion failed
(eq? (quote ("foo" "bar" "")) (string/split "foo-bar" "-"))
     |                        |
     |                        ("foo" "bar")
     ("foo" "bar" "")

First-class support for Lua tables

> { :foo 1
.   :bar 2 }
out = {"bar" 2 "foo" 1}

Friendly error messages

> (]
[ERROR] Expected ')', got ']'
  => <stdin>:[1:2 .. 1:2] ("]")
 1 │ (]
 ^... block opened with '('
 1 │ (]
  ^ ']' used here
> 

Getting started

We have a getting started guide to help you get set up. Or you can clone the repo and jump right in!

The website also contains documentation for all functions and macros, should you need to check how something works.

If you have any questions, would like to contribute or just feel like chatting, do join us in the #urn channel on FreeNode.

urn's People

Contributors

crazedprogrammer avatar emmachase avatar plt-amy avatar squiddev avatar t-mw avatar technomancy 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

urn's Issues

About error messages in 'for-pairs'

(for-pairs vars tbl &body)
Macro defined at lib/core/base.lisp:352:2

It seems like the existance if the 'tbl' element is not properly checked. It should fail if a variable that should represent 'tbl' is missing. Instead there is a bad argument error.

In the repl this works:

urn
> (for-pairs (k v ) gg (print! k))
[ERROR] Cannot find variable 'gg'
  => <stdin>:[1:19 .. 1:20]
 1 │ (for-pairs (k v ) gg (print! k)

But not in the script.

 cat test.urn 
(define xx (lambda (ll) 
   (let ((res ll)
         (mystruct (struct-literal :foo 123)))
     (for-pairs
       (k v)
       struct
       (push! res (list k v))
       res))))

(xx (list))

Error:
lua: out.lua:92: bad argument #1 to 'next1' (table expected, got function)
stack traceback:
        [C]: in function 'next1'
        out.lua:92: in function <out.lua:90>
        (...tail calls...)
        [C]: in ?

Support Unicode?

So this is a longshot and might not be easy due to Lua, but, I am trying to write a program that parses text files that are made up of, among other things, Greek letters. I had to write a hack to allow myself to parse λs by reading two bytes out of a string... but... I can't write hacks for the general case.

Is it possible to make it effortless to use Unicode characters in strings? Or at least, if not effortless, easy? I honestly have no knowledge of what would need to be done to support this.

For reference, this is the project I've been working on: https://git.sci4me.com/sci4me/lambda_calculus

EDIT: Really I just need to use the UTF-8 library, so, ... my concern with that is that it's not supported on LuaJIT. (And I have no idea how to do it and it seems nontrivial to change all of my code to support it but idk)

Redesigning the resolver

General planning issue for the rewritten resolver/macro expander. The current system is not well suited to several planned language features.

Macros returning multiple values (from unpack/splice/@)

We first need to think about how this is going to work. The obvious use case is returning multiple top top-level definitions:

(defmacro gen-things (name)
  @(`(define name "foo")
      `(define ,(string->symbol (.. (symbol->string) "/bar")))
(gen-things)

However, do we want to handle this in other cases places, such as function arguments or cond expressions. Consider:

(defmacro print-branch (case msg)
  @(,case `(print! ,msg)))
	
(cond
  ((print-branch x "Hello") ;; ...

should this expand to

(cond
  (x (print! "Hello") ;; ...

Also, should splice be allowed in the middle of argument lists, like variadic arguments on unquote-splice? Could we remove unquote-splice and just emulate it as a mixture of unquote and splice?

REPL

The current resolver heavily relies on yielding coroutines. Whilst this is fine as we constantly need to exit to recompile sections, it does make the implementation of the REPL more complex. If we could abstract the yielding out of the State and Scope objects (maybe as a callback argument) this would be great.

Top-level unquote

A feature 'borrowed' from Metalua, it would be really cool to have compile-only code: basically an implicit macro. This could be done through the use of ,. Obviously this would have the same restrictions as other quotes (only being able to capture top-level variables). Example:

(define people-names ,(io/read-all "names.txt"))

expands to

(define people-names "Kevin\nBruce\n")

This would then require assigning a state for arbitrary expressions, rather than just top level definitions (as we have to trace requirements). Maybe each node could have a immutable list of dependencies (built from a union of its children's dependencies).

Improved symbol "exposure"

Currently, symbols store a tag to their variable in the lookup table. In order to mutable variable metadata, we'd have to pass the variable lookup table at macro compile time. So `foo would look something like {tag = "symbol", contents = "foo", var = _vars["foo_hash"] }.

How to declare a method that uses a colon?

I am using a Lua 5.1 compiler which has several native objects that have methods that use colons. for example

local value = nativeObject:GenerateValue("someParameter")

So I would like to do this in urn:

(define value (GenerateValue "someParameter"))

But I don't know exactly how to do it. try to create a nativeObject.lisp file with this:

(define-native someParameter)

but I don't know what your meta file should look like.

Roadmap

Optimisations

  • Strip unused symbols
  • Constant propagation
  • Function inlining
  • Inline Lua operators
  • Convert tail recursive functions to loops
  • Don't emit native stuff when not needed

Stdlib/syntax

  • Pattern maching
  • Import with require instead.
  • Keys :foo for instance
  • Variadic arguments in the middle ((lambda (foo &bar baz) ...)).
  • Metatables on symbol, number and string. Can we handle true, false and nil symbols "intelligently" too?

Core

  • Statically bind symbols: hygenic macros
  • unquote in top level to "escape" compiler?
  • Variable metadata (export, documentation, etc...)

Other

  • Documentation generation
  • Typing
  • Deterministic compilation
  • Line mappings

Struct predicates shouldn't check for nil

I had a struct that had a field which was sometimes equal to nil. If any field of a struct is nil, the predicate seems to indicate that the value tested is not of that struct type.

To recreate:

(defstruct blah
    (fields
        (mutable a)))

(with (a (make-blah))
    (print! (blah? a))) ; prints false; I'd expect it to print true since the tag would be blah.

Grammar?

Is there a formal grammar description of Urn?

Create an documentation index

An index would be nice, something in the form of
[ name ] ([short description] - [ library name] )

Example for an entry in the alphabetic index:

A
- accumulate-with (A composition of reduce and map. - core/list)
- ...

And also a short overview over the libraries:

Libraries
-core/list: List manipualtion functions

Error in "Guess the number" tutorial

This line:

(import lua/os time)

should instead be:

(import lua/os (time))

Otherwise it fails silently with time being still undefined.

BTW, is there a better place to report errors in documentation than here?

Create a PKGBUILD file and publish urn as a AUR package

I and some friends (@kress95) have interests to have a package of urn for Arch Linux. We can build this as well, this issue is to track this implementation and if someone can contribute with this.

Thanks for making urn! It's a awesome project.

Roadmap

  • list dependencies
  • write a PKGBUILD
  • publish it on AUR

How to call `eval`?

Urn seems to emphasize compilation over interpretation. I appreciate that, but it's sometimes nice to call eval on quoted forms. Does eval exist or is it possible to define it using macros? Maybe I can treat the compilation environment as my runtime environment to allow this?

Standard bit package?

There is lua/bit32 and luajit/bit - can we get a standardized bit package that can be relied upon no matter which version of Lua is being used? Even if that might mean having to resort to implementing the bit operations in software, it would be really handy.

how does capturing multiple splices work?

i dont know if this is a bug or not
say i have two lists (list 1 99999) and (list 1 2) and would like to join them together into a single list easily

the easiest way i know of doing it is
> (flatten (list (list 1 99999) (list 1 2))) out = (1 99999 1 2)

another way is
> (with (res (list)) (for-each item (list 1 99999) (push! res item)) (for-each item (list 1 2) (push! res item)) res) out = (1 99999 1 2)

one way i thought would work, but doesn't, is
> (list @(list 1 99999) @(list 1 2)) out = (1 1 2)
even though (list @(list 1 99999) returns (list 1 99999)

why doesn't the last way work? is it a bug or am i misunderstanding how splice works in this situation

matching of quoted symbols

hi,
It seems like 'case' can match keywords (:key) cannot match quoted symbols ('sym). I think it would be nice if that would work too.

(define mycol 'Blue)

(case mycol
  ('Blue (putstr "blueeee"))
  (_ (putstr "deffff")))

Live coding with Love2d

I have not tested Urn yet, but my interest with Lisp is always games and live interactive development. The hurdle with Urn is, that it needs to be transpiled to Lua code. There is this little project, which will reload an exiting Love2d project and enable live coding.
Lets say Urn is parametrized to produce code expected by lovelive, mainly a module called app.lua with callbacks like app.load, app.update and app.darw, would we have an Urn Love2d live coding environment? What do you think? Of course this assumes that we have a process/editor which will trigger the transpiling...

allow different file extensions for source files

I use urn for scripts and in my script-dir are also files written in common lisp. Even if all my CL files end in .cl, it is still confusing when some files end in .lisp, especially if I want to share it someday. Imo this makes sense for sharing code in general to avoid confusion.

Unless there are any hard problems with making the file extension variable, it would be neat, if you could implement it. If you got no time / don't care enough, but would accept a pull request I might look into it as well and create one.

Alternatively changing it to .urn would also fix the issue, but this would afaik conflict with the current urn code base.

Overhauling the Optimiser

Just some thoughts on a redesigned optimiser framework.

Firstly, I'd really like to enable the optimiser framework to be split into "tagging" and "transforming". "Tagging" would traverse the tree and gather metadata, such as use-define chains, tail recursion and function metrics such as size. This data could then be used for analysis (warning about unused variables, optimising tail-recursive codegen, editing tools) and the optimiser transformations.

The actual optimiser will then take this data and use it to perform a series of transformations:

  • Stripping unused symbols
  • Constant folding and propagation
  • Function inlining

range :by is one too high?

core/list/range

To me it makes sense if
> (range :from 1 :to 10) out = (1 2 3 4 5 6 7 8 9 10)

was a shorthand for
> (range :from 1 :to 10 :by 1)

but it is not, it is a shorthand for
> (range :from 1 :to 10 :by 2) out = (1 2 3 4 5 6 7 8 9 10)

In fact, running
> (range :from 1 :to 10 :by 1)
causes an infinite loop which could be guarded against. Since it cant be negative may as well guard against :by (n < 1) too.

How to join two strings?

Is there something simpler then string/concat like

(join "hello " world)

here is my "fix":

(defmacro join (&strings)
   `(string/concat (list ,@strings) ""))

is there something better?

How to set an element in a list?

How do you set the ith element of a list? set-idx!? I don't know how the implementation works, but I see that nth takes ["n"] into account and set-idx! doesn't..

Shebang not portable

When I compile on my laptop with --shebang, I get #!/usr/bin/env C:\Users\sci4me\AppData\Local\LuaVM\versions\5.1\lua.exe whereas I get a totally different path on my desktop. Should it not just generate #!/usr/bin/env lua or something?

Generate for loops instead of while loops

According to my tests, using a for loop instead of a while loop is about 28% faster with Lua and 67% faster with LuaJit. Therefore, emitting for loops wherever possible would be a pretty significant performance win for the generated code, and would lead to slightly smaller code size too.

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.