Giter Site home page Giter Site logo

euwren's Introduction

hiii!!!

I'm liquidex, a friend living on the Internet. here's a little about me:

  • I'm a wizard of computers, practicioner of binary magic and bitcasting
  • I do various types of stuff ranging from code to art (and code is art too)
  • and the non-code art I do includes drawing and music

Come join the fun at https://liquidex.house, where you can read more about me (among other things!)

If you wanna chat, you can find my contact details here.

euwren's People

Contributors

liquidev 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

Watchers

 avatar  avatar  avatar  avatar  avatar

euwren's Issues

Constructor aliasing

Wren allows you to declare constructors with arbitrary names.

class MyClass {
  construct thing(x) {
    _x = x
  }
}

The support for this should be relatively easy to add.

Enums

Enum binding is not supported. Code like this:

type
  MyEnum = enum
    myA
    myB
    myC
wren.foreign("enum"):
  MyEnum

should automatically produce a module call to add the following code:

class MyEnum {
  static myA { 0 }
  static myB { 1 }
  static myC { 2 }
}

Aliasing should be supported through the NimName -> WrenName syntax.
Automatic prefix stripping should be supported through the MyEnum - prefix syntax.
These two should be combinable into MyEnum - prefix -> WrenEnum.

Numbers from Wren should be converted to enums. Type checking is not going to work, because it would require a big overhead, while enums are supposed to be lightweight and fast.

Allow for foreign types without constructors

Related: #7

Something similar to this should be allowed:

wren.foreign("module"):
  MyClass:
    {.noconstr.}

This should register the class as a foreign class in the generated module, but prevent construct new(…) {} from generating.

Improve type check messages

The current way these messages are reported is pretty bad, because it doesn't account for Wren types and aliased names. Example:

StrUtils.repeat(1, 1)
type mismatch: got <number, number>
but expected one of:
  getRepeated(str: string, n: int)

The proc name is the Nim name and not the Wren name. str: string is correct, but n: int should be n: number to avoid confusion.

Documentation generator

It would be nice to have a doc generator for euwren modules. Syntax example:

import macros, os
const Docs = getProjectPath()/"docs"

wren.foreign("myModule"):
  # output is markdown for easy piping to pandoc or other renderers
  # (open to consideration, may use html or rst in the end)
  {.doc: Docs/"myModule.md".}
  MyClass:
    ## This class implements some methods.
    sayHello # doc comment is inherited from the proc
    printNumber ## This proc prints a number.
    # ↑ doc comment is specified explicitly
    *newMyClass -> new
    ?someField

Example of generated output:

## module `myModule`

### index
 - class `MyClass` (with a link to the class)

### class `MyClass`

#### `sayHello()`

Prints hello to the console.

#### `printNumber(x: number)`

This proc prints a number.

#### static `new()`

> no documentation provided

#### `someField`

> no documentation provided

A macro specifying generation options could be provided.

euwren.docOpt:
  format = "html"
  stylesheet = slurp("docStyle.css")
  # etc

Tuples

euwren currently cannot wrap tuples. Tuples should automatically have a constructor, so that no extra wrappers are needed. Example:

tuple MyTuple = tuple[a, b, c: int]

wren.foreign("tuples"):
  MyTuple: discard
import "tuples" for MyTuple

var x = MyTuple.new(1, 2, 3)
System.print(x.a)
x.b = 10
System.print(x.b)
// etc

Symbol aliasing

Currently, it's impossible to alias a symbol to another one. While documented, this syntax:

myProc(int) -> wrenProc

is invalid.

Foreign types are not supported as return types from foreign procs

This won't work:

type
  Test = ref object

var test: Test

proc getTest(): Test = test

wren.foreign("test"):
  Procs:
    getTest
wren.ready()

wren.run("""
  import "test" for Procs

  var t = Procs.getTest()
""")

It will fail with a compile error, stating that Test is an unsupported type for slot assignment. Instead, the type should be checked if it's registered in the VM—if not, throw an error.

Build fails when using --gc:arc.

Hi! Sorry if this bug report doesn't follow any predefined template or guidelines as I didn't notice any info on one. Nim just updated the other day and moved the new arc GC to stable, so I am currently trying to prep a prototype in-house game engine tinker project to take advantage of it, which uses euwren for the scripting layer. Unfortunately, the build fails with:

# Resetting /home/[OMITTEDHOMEDIRNAME]/.cache/nim/nimterop/euwren
# Importing /home/[OMITTEDHOMEDIRNAME]/.cache/nim/nimterop/euwren/src/include/wren.h
fatal.nim(49)            sysFatal
Error: unhandled exception: 'sym' is not accessible using discriminant 'kind' of type 'TNode' [FieldError]

If I build with the default garbage collector or remove euwren and use arc, there appears to be no problem, so I'm fairly certain this isn't on my end (though if it is I do apologize for taking up your time). I don't mean to rush you or anything since this update just dropped (as of this report) yesterday, and frankly I can just disable that component on my end for now. I just wanted to make sure you were aware of the issue 😄

Exception handling in foreign procs

Any exceptions thrown by foreign procs should be handled and produce a wrenAbortFiber call. Currently, this isn't done out of performance concerns, but I'll do some benchmarks to see whether the cost of a try…except block is that significant.

List support

Lists are not supported for slot assignment/retrieval, making array | seq parameters not bindable.

Generate foreign binding code implicitly

Currently, when binding code, you're expected to provide the Wren module yourself. A better solution would be to generate the code implicitly. This shows how such code could be generated:

wren.foreign("hello"):
  WrenGreeter -> Greeter: # foreign class Greeter {
    [new] newGreeter      #   construct new() {}
    `target=`             #   foreign target=(target)
    [get] greeting        #   foreign greeting
                          # }

Arbitrary code may be injected by adding a string literal:

wren.foreign("hello"):
  WrenGreeter -> Greeter: # foreign class Greeter {
    [new] newGreeter      #   construct new() {}
    `target=`             #   foreign target=(target)
    [get] greeting        #   foreign greeting
    """
    greet() {
      System.print("Hello, " + greeting)
    }
    """                   #   // <injected string>
                          # }

module injections are still allowed:

wren.foreign("hello"):
  WrenGreeter -> Greeter: # foreign class Greeter {
    [new] newGreeter      #   construct new() {}
    `target=`             #   foreign target=(target)
    [get] greeting        #   foreign greeting
    """
    greet() {
      System.print("Hello, " + greeting)
    }
    """                   #   // <injected string>
                          # }
  module """
    class Greeters {
      static world() {
        var greeter = Greeter.new()
        greeter.target = "world"
        return greeter
      }
    }
  """

Explicit static methods

Currently, it's not possible to enforce a method to be static. For a method to be implicitly static, one of the following must be true:

  • the proc being bound must not be part of an object
  • the proc being bound must not have its class as its first parameter
    This could be solved by adding a pragma like {.wrenStatic.} to procs, or, an attribute like [staticProc] during binding, to mark the bound proc as explicitly static.

Code restructuring

Turns out generating code directly from the input AST passed to foreign() is very messy. Instead, an intermediate structure should be created first, and code should be generated using that.

Inheritance in type checking

Currently, this will fail:

import euwren

type
  A = ref object of RootObj
  B = ref object of A

proc newA(): A = A()
proc newB(): B = B()

proc print(a: A) = echo "A"

var wren = newWren()
wren.foreign("inherit"):
  A:
    [new] newA
    print
  B:
    [new] newB
  module """
    foreign class A {
      construct new() {}
      foreign static print() // hypothetically, this won't work too, but let's ignore the details
    }
    foreign class B {
      construct new() {}
    }
  """
wren.run("""
import "inherit" for A, B

var b = B.new()
A.print(b) // type mismatch
""")

It should print A, and not fail with a type mismatch, since B inherits from A on the Nim side.

Inline procs

Add the ability to create "inline" procs:

wren.foreign("example"):
  [Inline]:
    myProc do (a: int) -> float:
      result = float(a) * 1.5

This would get rewritten to something like this:

# not the actual result, just for illustration purposes
proc myProc(a: int): float =
  result = float(a) * 1.5
wren.foreign("example"):
  [Inline]:
    myProc

but myProc would stay in the scope of the foreign call, and not be visible outside.

The do syntax is used because it allows me to keep the * and ? as prefix operators:

*myProc do (): discard

Otherwise, they must've been used as postfix in the proc declaration:

proc myProc*() = discard

but as you can see, it's confusing. * is Nim's export marker, but in this instance, it would make the proc static. Also, it would make parsing it more difficult, since the proc declaration has to be parsed as a special case .

This can solve the issue of euwren not being able to resolve certain weird cases of overloads, but most notably, it allows one to bind things like variables without using intermediary procs declared outside of foreign(), cluttering up the namespace:

var a = 1
wren.foreign("example"):
  [Vars]:
    a do () -> int: a

A different syntax like a: int might be considered for binding consts and variables in the future.

It's important to note that do: and do (): are different syntax. The first creates a nnkStmtList, and the other creates an nnkDo. Statement lists will be used for multiple overloads in the future, so do: would not be supported in this feature.

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.