• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

richardhundt/shine: A Shiny Lua Dialect

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称(OpenSource Name):

richardhundt/shine

开源软件地址(OpenSource Url):

https://github.com/richardhundt/shine

开源编程语言(OpenSource Language):

Lua 88.3%

开源软件介绍(OpenSource Introduction):

-- taken from a real world web app which talks to elasticsearch

import JSON        from "codec.json"
import Presenter   from "swarm.app"
import HTTPStatic  from "swarm.static"

import ESClient, ESIndex from "app.es.client"

-- this presenter is a singleton, so we use `module` here
module Features include Presenter

   local client  = ESClient('localhost', 9200)
   local storage = ESIndex(client, 'features')
   local static  = HTTPStatic("./public/admin")

   @self.route('GET', '/admin')
   admin(req, path, query)
      if #path == 0 then
         path[#path + 1] = 'index.html'
      end
      return static.handler(req, path, query)
   end

   @self.route('GET', '/features/feature/:id')
   fetch(req, args, query)
      resp = storage.lookup('feature', args['id'])
      hdrs = { }
      hdrs['Content-Type'] = 'application/json'
      if resp.exists then
         return { 200, hdrs, { JSON.encode(resp) } }
      else
         return { 404, hdrs, { JSON.encode(resp) } }
      end
   end

end

Shine Reference

This document aims to be a fairly concise, but broad reference.

To help get you started, though, have a look at the wiki which has a growing collection of tutorials.

Introduction

Shine is a general purpose, dynamic, multi-paradigm programming language which is based on, and extends, Lua with features geared more to programming in the large. For maximum performance it uses a modified version of the LuaJIT virtual machine, which is known for its small footprint and impressive performance which rivals that of C.

Most of the language features are those of the underlying LuaJIT VM, and the extensions are implemented in terms of lower level constructs already present in LuaJIT, so anyone familiar with Lua and LuaJIT should quickly feel right at home with Shine.

Moreover, vanilla Lua libraries can be loaded and run unmodified, and although compilation of Lua code is slower than with LuaJIT or PuC Lua, there is no additional runtime penalty, and since they share the same bytecode, this means that Shine users can leverage all of the existing Lua libraries out there without writing wrappers or additional bindings. You can, of course, call seamlessly into Shine code from Lua too, as long as Lua is running within the Shine runtime.

Another goal of Shine, is that the standard libraries which are included are focused on providing CSP style concurrency, so although the language itself has no concurrency primitives, the fibers, channels, threads, pipes and I/O libraries are all centered around building highly scalable, low-footprint, network oriented concurrent applications.

Philosophy

Shine strives for a pragmatic balance between safety and syntactic and semantic flexibility.

Summary of safety features:

  • Static local variable name resolution.
  • Default declaration scope is localized.
  • Function and method parameter guards.
  • Scoped variable guards.
  • Self-type assertions in methods.

Summary of flexibility features:

  • Operator meta-methods as in Lua with Shine specific extensions.
  • Deep introspection into nominal types.
  • Lambda expressions (short function syntax).
  • Optional parentheses.
  • Property getters and setters with fallback.
  • User-only operators.
  • Procedural Macros
  • Decorators

Additionally, all constructs can be nested. Classes can be declared inside other classes and even inside functions, which allows for run-time construction of classes, modules and grammars.

Other notable extensions to Lua:

  • Bitwise operators.
  • Standard libraries.
  • Tight LPeg integration.
  • Classes and modules .
  • continue statement.
  • try, catch, finally
  • Destructuring assignment.
  • Pattern matching.
  • Default function parameters.
  • Parameter and variable guards.
  • Richer type system.
  • Concurrency primitives
  • OS Threads

Getting Started

Shine ships with all its dependencies, so simply clone the git repository and run make && sudo make install:

$ git clone --recursive https://github.com/richardhundt/shine.git
$ make && sudo make install

Standard package.path and package.cpath values set up by virtual machine could be customized with passing TVMJIT_PREFIX and TVMJIT_MULTILIB parameters to make. It could be useful for accessing Lua libraries installed by standard Linux distro way. For example, on Debian following parameter values could be used:

$ make TVMJIT_PREFIX=/usr TVMJIT_MULTILIB="lib/$(gcc -print-multiarch)" && sudo make install

This will install two executables shinec and shine (along with standard library). The shinec executable is just the compiler and has the following usage:

usage: shinec [options]... input output.
Available options are:
  -t type   Output file format.
  -b        List formatted bytecode.
  -n name   Provide a chunk name.
  -g        Keep debug info.
  -p        Print the parse tree.
  -o        Print the opcode tree.

The main executable and runtime is shine, which includes all the functionality of shinec via the -c option and has the following usage:

usage: shine [options]... [script [args]...].
Available options are:
  -e chunk  Execute string 'chunk'.
  -c ...    Compile or list bytecode.
  -i        Enter interactive mode after executing 'script'.
  -v        Show version information.
  --        Stop handling options. 
  -         Execute stdin and stop handling options.

The script file argument extension is significant in that files with a .lua extension are passed to the built-in Lua compiler, whereas files with a .shn extension are compiled as Shine source.

Shine has been tested on Linux and Mac OS X, and support for Windows is in progress.

Language Basics

Shine is a line-oriented language. Statements are generally seperated by a line terminator. If several statements appear on the same line, they must be may be separated by a semicolon ;.

Long expressions with infix operators may be wrapped with the operator leading after the line break, however for function and method calls, the argument list must start on the same line as the callee:

a = 40
  + 2 -- OK

foo
  .bar() -- OK

print "answer:", 42 -- OK [means: print("answer:", 42)]

print(
  "answer:", 42 -- OK
)

print
  "answer:", 42 -- BAD [syntax error]

A bare word by itself is parsed as a function call:

if waiting then
   yield -- compiles as yield()
end

Comments

Comments come in 3 forms:

  • Lua-style line comments starting with --
  • Lua-style block comments of the form --[=*[ ... ]=*]
  • Shine block comments of the form --:<token>([^)]*)?: ... :<token>:

The last form is designed to allow Shine sources to be annotated for processing by external tools such as documentation generators:

-- a line comment

--[[
a familiar Lua-style
block comment
]]

--::
simple Shine block comment
::

--:md(github-flavored):
perhaps this is extracted by a markdown processor
:md:

Identifiers

Identifiers must start with [a-zA-Z_$?] (alphabetical characters or _ or $ or ?) which may be followed by zero or more [a-zA-Z_$!?0-9] characters (alphanumeric or _ or $ or ! or ?).

$this_is_valid
so_is_$this
this_is_valid_too!
unusual?_but_still_valid

Scoping

Shine scoping rules are similar to Lua's with the addition of the concept of a default storage if local is not specified when introducing a new declaration.

The default storage for variables is always local. For other declarations, it depends on the enclosing scope:

  • If at the top level of a compilation unit, the default is package scoped for all non-variable declarations.
  • Inside class and module bodies, the default is scoped to the body environment.
  • Everwhere else (i.e. functions and blocks) it is local.

Declarations can always be lexically scoped by declaring them as local.

In short, what this means is that most of the time you can simply leave out the local keyword without worrying about creating globals or leaking out declarations to the surrounding environment.

There are cases where a local is useful for reusing a variable name in a nested scope when the inner scope should shadow the variable in the outer scope.

Builtin Types

Shine includes all of LuaJIT's builtin types:

  • nil
  • boolean
  • number
  • string
  • table
  • function
  • thread (coroutine)
  • userdata
  • cdata

All other extensions are built primarily using tables and cdata.

These include:

Primitive meta types:

  • Nil
  • Boolean
  • Number
  • String
  • Table
  • Function
  • Coroutine
  • UserData
  • CData

Additional builtins:

  • null (CData meta type)
  • Array
  • Range
  • Error

Nominal meta types:

  • Class
  • Module
  • Grammar

Pattern matching meta types:

  • Pattern
  • ArrayPattern
  • TablePattern
  • ApplyPattern

The meta meta type:

  • Meta

Booleans

The constants true and false. The only values which are logically false are false and nil. Everything else evaluates to true in a boolean context.

Numbers

The Shine parser recognizes LuaJIT cdata long integers as well as Lua numbers. This is extended with octals.

The following are represented as Lua number type:

  • 123
  • 123.45
  • 1.2e3
  • 0x42 - heximal
  • 0o644 - octal

These are LuaJIT extensions enabled by default in Shine:

  • 42LL - LuaJIT long integer cdata
  • 42ULL - LuaJIT unsigned long integer cdata

nil and null

The constant nil has exactly the same meaning as in Lua.

The constant null is exactly the cdata NULL value. It compares to nil as true, however, unlike in C, in a boolean context also evaluates to true. That is, although null == nil holds, if null then print 42 end will print "42".

Strings

As in Lua, strings are 8-bit clean, immutable and interned. The extensions added by Shine relate to delimiters, escape sequences and expression interpolation.

There are two types of strings:

  • Single quoted
  • Double quoted

Single quoted strings are verbatim strings. The only escape sequences which they recognise are \' and \\. All other bytes are passed through as is.

Double quoted strings allow the common C-style escape sequences, including unicode escapes. These are all valid strings:

"text"
"tab\t"
"quote\""
"\x3A"      -- hexadecimal 8-bits character
"\u20AC"    -- unicode char UTF-8 encoded
"
multiline
string
"

Additionally, double quoted strings interpolate expressions inside %{ and } marks. When constructing strings programmatically it is encouraged to use this form as the fragments produced are concatenated in a single VM instruction without creating short-lived temporary strings.

"answer = %{40 + 2}" -- interpolated

Both types of strings can be delimited with single and triple delimiters. This is just a convenience to save escaping quotation marks:

"a quoted string"
"""a triple quoted string"""
'a verbatim string'
'''a triple quoted verbatim string'''
String Operations

Strings have a metatable with all the methods supported by Lua, namely: len, match, uppper, sub, char, rep, lower, dump, gmatch, reverse, byte, gsub, format and find.

Shine adds a split method with the following signature:

  • str.split(sep = '%s+', max = math::huge, raw = false)

Splits str on sep, which is interpreted as a Lua match pattern unless raw is true. The default value for sep is %s+. If max is given, then this is the maximum number of fragments returned. If the pattern is not found, the split returns the entire string. The split method is only available as a method, and is not defined in the string library.

Strings can additionally be subscripted with numbers and ranges. Both subscripts have the effect of calling string.sub() internally. If a number is passed then a single character is returned at that offset, otherwise the two extremes of the range are passed to string.sub. Negative offsets may be used.

Ranges

Ranges are objects which represent numeric spans and are constructed with <expr> .. <expr>. They can be used for slicing strings, or for looping constructs.

Tables

Tables work just as in Lua with the addition that line-breaks serve as separators, in addition to , and ;

-- these are equivalent
t = { answer = 42, 1, 'b' }
t = {
    answer = 42
    1
    'b'
}

Arrays

Arrays are zero-based, numerically indexed lists of values, based on Lua tables, with the difference being that they track their length, and so may contain nil values.

Arrays have a literal syntax delimited by [ and ] as in languages such as Perl, JavaScript or Ruby, but may also be constructed by calling Array(...).

a = [ 1, 'two', 33 ]
a = Array(1, 'two', 33) -- same thing

The array type defines the following methods:

  • join(sep = '')

  • push(val)

  • pop()

  • shift()

  • unshift(val)

  • slice(offset, count)

    Returns a new array with count elements, starting at offset.

  • reverse()

    Returns a new array with elements in reverse order.

  • sort(cmp = less, len = #self)

    Sorts the array in place using the comparison function cmp if given and limits the number of items sorted to len if provided.

Patterns

Shine integrates Parsing Expression Grammars into the language as a first-class type, thanks to the excellent LPeg library.

Patterns are delimited by a starting and ending /, which borrows from commonly used regular expression quotation. However, Shine's patterns are very different and far more powerful than regular expressions.

Syntax borrows heavily from the re.lua module. Notable differences are that | replaces re's / as an alternation separator, %pos replaces {} as a position capture, and +> replaces => as a match-time capture. Fold captures are added using ~>.

Unlike the re.lua module, grammars aren't represented as strings, but are compiled to LPeg API calls directly, so production rules for ->, ~> and +> captures can be any term or postfix expression supported by the language itself.

Which means you can say rule <- %digit -> function(c) ... end with functions inlined directly in the grammar.

It also means that patterns can be composed using the standard Lua operators as with the LPeg library:

word = / { %alnum+ } /
patt = / %s+ / * word + function()
   error "d'oh!"
end

Expressions

Expressions are much the same as in Lua, with a few changes and some additions, notable C-style bitwise operators.

String concatenation is done using ~ instead of .. as the latter is reserved for ranges. The ** operator replaces ^ as exponentiation (^ is bitwise xor instead).

Moreover, methods can be called directly on literals without the need to surround them in parentheses. So whereas in Lua one would say:

local quote = ("%q"):format(val)

in Shine it's simply:

local quote = "%q".format(val)

Operators

The following table lists Shine's operators in order of increasing precedence. Operators marked with a trailing _ are unary operators.

Operator Precedence Associativity Comment
..._ 1 right unpack
or 1 left logical or
and 2 left logical and
== 3 left equality
!= 3 left inequality
~~ 3 left matches
!~ 3 left does not match
is 4 left type equality
as 4 left type coercion
>= 5 left greater than or equal to
<= 5 left less than or equal to
> 5 left greater than
< 5 left less than
| 6 left bitwise or
^ 7 left bitwise exclusive or
& 8 left bitwise and
<< 9 left bitwise left shift
>> 9 left bitwise right shift
>>> 9 left bitwise arithmetic right shift
~ 10 left concatenation
+ 10 left addition
- 10 left subtraction
.. 10 right range
* 11 left multiplication
/ 11 left division
% 11 left remainder (modulo)
~_ 12 right bitwise not
!_ 12 right logical not
not_ 12 right logical not
** 13 right exponentiation
#_ 14 right length of

Many infix operators are also available in update, or assigment, form:

Operator Comment
+= add assign
-= subtract assign
~= concatenate assign
*= multiply assign
/= divide assign
%= modulo assign
**= exponentiation assign
and= logical and assign
or= logical or assign
&= bitwise and assign
|= bitwise or assign
^= bitwise xor assign
<<= bitwise left shift assign
>>= bitwise right shift assign
>>>= bitwise arithmetic right shift assign

The following operators are used in patterns:

Operator Precedence Associativity Comment
~> 1 left fold capture
-> 1 left production capture
+> 1 left match-time capture
| 2 left ordered choice
&_ 3 right lookahead assertion
!_ 3 right negative lookahead assertion
+ 3 left one or more repetitions
* 3 left zero or more repetitions
? 3 left zero or one
^+ 4 right at least N repetitions
^- 4 right at most N repetitions

Not listed above are the common ( and ) for grouping, and postcircumfix () and [] operators for function/method calls and subscripting respectively.

In addition to the built-in operators, Shine exposes a full suite of user-definable operators, which share precedence with their built-in counterparts, but have no intrinsic meaning to the language.

Shine will simply try to call the corresponding meta-method as with the built-in operators. The full listing is:

Operator Precedence Associativity Meta-Method
:! 3 left __ubang
:? 3 left __uques
:= 5 left __ueq
:> 5 left __ugt
:< 5 left __ult
:| 6 left __upipe
:^ 7 left __ucar
:& 8 left __uamp
:~ 10 left __utilde
:+ 10 left __uadd
:- 10 left __usub
:* 11 left __umul
:/ 11 left __udiv
:% 11 left __umod

Call Expressions

When calling a function, method, or other callable, parenthesis may be omitted provided there is either at least one argument. The following are all valid:

fido.bark(loudness)
fido.move x, y          -- fido.move(x, y)

As a special case, if the callee is a single word as a statement on its own, then no parentheses or arguments are required:

yield                   -- OK yield()
fido.greet              -- BAD (not a word)
print yield             -- BAD (yield not a statement)

Member Expressions

Member expressions have three lexical forms:

  • method call
  • property call
  • property access

Shine deviates from Lua in that the . operator when followed by a call expression is a method call, whereas Lua uses :. To call a property without passing an implicit receiver, use :: instead:

s.format(...)          -- method call (self is implicit)
string::format(s, ...) -- property call

For property access, either :: or . may be used with identical semantics. By convention, one may prefer :: for indicating namespace access such as async::io::StreamReader, whereas . may be used to access instance members.

Assignment

Assignment expressions are based on Lua allowing multiple left and right hand sides. If an identifier on the left is previously undefined, then a new local variable is automatically introduced the fist time it is assigned. This prevents global namespace pollution. Examples:

a, b = 1, 2             -- implicit locals a, b
o.x, y = y, o.x         -- implicit local y 
local o, p = f()        -- explicit
a[42] = 'answer'

Destructuring

Shine also supports destructuring of tables, arrays and application patterns. This can be used during assignment as well as pattern matching in given statements.

For tables and arrays, Shine knows how to extract values for you:

a = ['foo', { bar = 42 }, 'baz']
[x, { bar = y }, z] = a
print x, y, z           -- prints: foo  42  baz

However, with objects you have to implement an __unapply hook to make it work. The hook is expected to return an iterator which would be valid for use in a generic for loop. Here's an example:

class Point
   self(x = 0, y = 0)
      self.x = x
      self.y = y
   end
   function self.__unapply(o)
      return ipairs{ o.x, o.y }
   end
end

p = Point(42, 69)
Point(x, y) = p
print x, y              -- prints: 42   69

Patterns already implement the relevant hooks, so the following works as expected:

Split2 = / { [a-z]+ } %s+ { [a-z]+ } /
str = "two words"

Split2(a, b) = str

assert a == 'two' and b == 'words'

Comprehensions

Comprehensions are an experimental feature only implemented for arrays currently. They are also not lazy generators. They should look familiar to Python programmers. Here are two examples:

a1 = [ i * 2 for i in 1..10 if i % 2 == 0 ]
a2 = [ i * j for i in 1..5 for j in 1..5 ]

Lambda Expressions

Shine has a syntactic short-hand form for creating functions:

(<param_list>)? => <func_body> <end>

The parameter list is optional.

-- these two are identical
f1 = (x) =>
   return x * 2
end
function f1(x)
    return x * 2
end

Additionally, if the function body contains a single expression and appears on one line, then an implicit return is inserted and the end token is omitted:

b = a.map((x) => x * 2)

-- shorter still
b = a.map (x) => x * 2

Means:

b = a.map((x) =>
   return x * 2
end)

Statements

Do Statement

do <chunk> end

Same as in Lua.

If Statement

if <expr> then <chunk> (elseif <expr> then <chunk>)* (else <chunk>)? end

Same as in Lua.

上一篇:
k5dash/LuaDota2: Scrips发布时间:2022-08-16
下一篇:
kyazdani42/nvim-web-devicons: lua `fork` of vim-web-devicons for neovim发布时间:2022-08-16
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap