aria

所属分类:人工智能/神经网络/深度学习
开发工具:GO
文件大小:0KB
下载次数:0
上传日期:2017-08-05 21:16:21
上 传 者sh-1993
说明:  表达的、无声的、解释的、玩具编程语言
(Expressive, noiseless, interpreted, toy programming language)

文件列表:
LICENSE (1068, 2017-08-05)
aria.go (2588, 2017-08-05)
ast/ (0, 2017-08-05)
ast/ast.go (19346, 2017-08-05)
build.sh (905, 2017-08-05)
glide.lock (713, 2017-08-05)
glide.yaml (144, 2017-08-05)
interpreter/ (0, 2017-08-05)
interpreter/interpreter.go (44431, 2017-08-05)
interpreter/interpreter_test.go (5365, 2017-08-05)
interpreter/runtime.go (6338, 2017-08-05)
interpreter/scope.go (1413, 2017-08-05)
interpreter/scope_test.go (1785, 2017-08-05)
interpreter/types.go (4721, 2017-08-05)
lexer/ (0, 2017-08-05)
lexer/lexer.go (11928, 2017-08-05)
lexer/lexer_test.go (6470, 2017-08-05)
lexer/symbol.go (477, 2017-08-05)
lexer/symbol_test.go (696, 2017-08-05)
library/ (0, 2017-08-05)
library/dict.ari (1022, 2017-08-05)
library/enum.ari (1736, 2017-08-05)
library/library.go (8587, 2017-08-05)
library/math.ari (1053, 2017-08-05)
library/string.ari (4177, 2017-08-05)
library/type.ari (343, 2017-08-05)
parser/ (0, 2017-08-05)
parser/parser.go (28791, 2017-08-05)
parser/parser_test.go (13133, 2017-08-05)
parser/precedence.go (1144, 2017-08-05)
reader/ (0, 2017-08-05)
reader/buffer.go (14191, 2017-08-05)
reader/reader.go (1013, 2017-08-05)
reader/reader_test.go (734, 2017-08-05)
reporter/ (0, 2017-08-05)
reporter/reporter.go (748, 2017-08-05)
reporter/reporter_test.go (1021, 2017-08-05)
token/ (0, 2017-08-05)
... ...

[![GoDoc](https://godoc.org/github.com/fadion/aria?status.svg)](https://godoc.org/github.com/fadion/aria) [![Go Report Card](https://goreportcard.com/badge/github.com/fadion/aria)](https://goreportcard.com/report/github.com/fadion/aria) # Aria Language Aria is an expressive, interpreted, toy language built as an exercise on designing and interpreting a programming language. It has a noiseless syntax, free of useless semi colons, braces or parantheses, and treats everything as an expression. Technically, it's built with a hand written lexer and parser, a recursive decent one (Pratt), and a tree-walk interpreter. I have never set any goals for it to be either fast, nor bulletproof, so don't expect neither of them. It features mutable and immutable values, if and switch conditionals, functions, type hinting, for loops, modules, the pipe operator, imports and many more. More importantly, it's getting expanded frequently with new features, more functions for the standard library and bug fixes. All of that while retaining it's expressiveness, clean syntax and easy of use. ```swift var name = "aria language" let expressive? = func (x: String) -> String if x != "" return "expressive " + x end "sorry, what?" end let pipe = name |> expressive?() |> String.capitalize() println(pipe) // "Expressive Aria Language" ``` ## Table of Contents * [Usage](#usage) * [Run a Source File](#run-a-source-file) * [REPL](#repl) * [Variables](#variables) * [Constants](#constants) * [Type Lock](#type-lock) * [Data Types](#data-types) * [String](#string) * [Atom](#atom) * [Int](#int) * [Float](#float) * [Boolean](#boolean) * [Array](#array) * [Dictionary](#dictionary) * [Nil](#nil) * [Type Conversion](#type-conversion) * [Type Checking](#type-checking) * [Operators](#operators) * [Shorthand Assignment](#shorthand-assignment) * [Functions](#functions) * [Type Hinting](#type-hinting) * [Default Parameters](#default-parameters) * [Return Statement](#return-statement) * [Variadic](#variadic) * [Arrow Functions](#arrow-functions) * [Closures](#closures) * [Recursion](#recursion) * [Tricks](#tricks) * [Conditionals](#conditionals) * [If](#if) * [Ternary Operator](#ternary-operator) * [Switch](#switch) * [Pattern Matching](#pattern-matching) * [For Loop](#for-loop) * [Range Operator](#range-operator) * [Pipe Operator](#pipe-operator) * [Immutability](#immutability) * [Modules](#modules) * [Imports](#imports) * [Comments](#comments) * [Standard Library](#standard-library) ## Usage If you want to play with the language, but have no interest in toying with its code, you can download a built binary for your operating system. Just head to the [latest release](https://github.com/fadion/aria/releases/latest) and download one of the archives. The other option, where you get to play with the code and run your changes, is to `go get github.com/fadion/aria` and install it as a local binary with `go install`. Obviously, you'll need `GOROOT` in your path, but I guess you already know what you're doing. ### Run a source file To run an Aria source file, give it a path relative to the current directory. ``` aria run path/to/file.ari ``` ### REPL As any serious language, Aria provides a REPL too: ``` aria repl ``` ## Variables Variables in Aria start with the keyword `var`. Accessing an undeclared variable, in contrast with some languages, will not create it, but instead throw a runtime error. ```swift var name = "John" var married = false var age = 40 age = 41 ``` Names have to start with an alphabetic character and continue either with alphanumeric, underscores, questions marks or exclamation marks. When you see a question mark, don't confuse them with optionals like in some other languages. In here they have no special lexical meaning except that they allow for some nice variable names like `is_empty?` or `do_it!`. ### Constants Constants have the same traits as variables, except that they start with `let` and are immutable. Once declared, reassigning a constant will produce a runtime error. Even data structures are locked into immutability. Elements of an Array or Dictionary can't be added, updated or removed. ```swift let name = "Ben" name = "John" // runtime error ``` ### Type Lock Type lock is a safety feature of mutable variables. Once they're declared with a certain data type, they can only be assigned to that same type. This makes for more predictable results, as an integer variable can't be assigned to a string or array. In this regard, Aria works as a strong typed language. This will work: ```swift var nr = 10 nr = 15 ``` This won't: ```swift var nr = 10 nr = "ten" // runtime error ``` ## Data Types Aria supports 7 data types: `String`, `Atom`, `Int`, `Float`, `Bool`, `Array`, `Dictionary` and `Nil`. ### String Strings are UTF-8 encoded, meaning that you can stuff in there anything, even emojis. ```swift let weather = "Hot" let price = "円500" ``` String concatenation is handled with the `+` operator. Concats between a string and another data type will result in a runtime error. ```swift let name = "Tony" + " " + "Stark" ``` Additionally, strings are treated as enumerables. They support subscripting and iteration in `for in` loops. ```swift "howdy"[2] // "w" ``` Escape sequences are there too if you need them: `\"`, `\n`, `\t`, `\r`, `\a`, `\b`, `\f` and `\v`. Nothing changes from other languages, so I'm sure you can figure out by yourself what every one of them does. ```swift let code = "if(name == \"ben\"){\n\tprint(10)\n}" ``` ### Atom Atoms, or symbols as some languages refer to them, are constants where the name is their value. Although they behave a lot like strings and can generally be interchanged, internally they are treated as their own type. As the language progresses, Atoms will be put to better use. ```swift let eq = :dog == :cat let arr = ["dog", :cat, :mouse] let dict = [:name => "John", :age => 40] let concat = "hello" + :world ``` They're interesting to use as control conditions, emulating enums as a fixed, already-known value: ```swift let os = "linux" switch os case :linux println("FREE") case :windows println("!FREE") end ``` ### Int Integers are whole numbers that support most of the arithmetic and bitwise operators, as you'll see later. They can be represented also as: binary with the 0b prefix, hexadecimal with the 0x prefix and octal with the 0o prefix. ```swift let dec = 27 let oct = 0o33 let hex = 0x1B let bin = 0b11011 let arch = 2 ** 32 ``` A sugar feature both in Integer and Float is the underscore: ```swift let big = 27_000_000 ``` It has no special meaning, as it will be ignored in the lexing phase. Writing `1_000` and `1000` is the same thing to the interpreter. ### Float Floating point numbers are used in a very similar way to Integers. In fact, they can be mixed and matched, like `3 + 0.2` or `5.0 + 2`, where the result will always be a Float. ```swift let pi = 3.14_159_265 let e = 2.71828182 ``` Scientific notation is also supported via the `e` modifier: ```swift let sci = 0.1e3 let negsci = 25e-5 ``` ### Bool It would be strange if this data type included anything else except `true` and `false`. ```swift let mad = true let genius = false ``` Expressions like the `if/else`, as you'll see later, will check for values that aren't necessarily boolean. Integers and Floats will be checked if they're equal to 0, and Strings, Arrays and Dictionaries if they're empty. These are called `truthy` expressions and internally, will be evaluated to boolean. ### Array Arrays are ordered collections of any data type. You can mix and match strings with integers, or floats with other arrays. ```swift let multi = [5, "Hi", ["Hello", "World"]] let names = ["John", "Ben", 1337] let john = names[0] let leet = names[-1] ``` Individual array elements can be accessed via subscripting with a 0-based index: ```swift let names = ["Kirk", "Bones", "Spock"] let first = names[0] // "Kirk" let last = names[-1] // "Spock" ``` In the same style, an index can be used to check if it exists. It will return `nil` if it doesn't: ```swift if names[10] // handle it end ``` Individual elements can be reassigned on mutable arrays: ```swift var numbers = [5, 8, 10, 15] numbers[1] = 7 ``` Appended with an empty or placeholder index: ```swift numbers[] = 100 numbers[_] = 200 // Same. ``` Arrays can be compared with the `==` and `!=` operators, which will check the position and value of every element of both arrays. Equal arrays should have the same exact values in the same position. They can also be combined with the `+` operator, which adds the element of the right side to the array on the left side. ```swift let concat = ["an", "array"] + ["and", "another"] // ["an", "array", "and", "another"] ``` Oh and if you're that lazy, you can ommit commas too: ```swift let nocomma = [5 7 9 "Hi"] ``` ### Dictionary Dictionaries are hashes with a key and a value of any data type. They're good to hold unordered, structured data: ```swift let user = ["name" => "Dr. Unusual", "proffesion" => "Illusionist", "age" => 150] ``` I'd argue that using Atoms for keys would make them look cleaner: ```swift let user = [:name => "Dr. Unusual", :proffesion => "Illusionist", :age => 150] ``` Unlike arrays, internally their order is irrelevant, so you can't rely on index-based subscripting. They only support key-based subscripting: ```swift user["name"] // "Dr. Unusual" ``` Values can be reassigned or inserted by key on mutable dictionaries: ```swift var numbers = ["one" => 1, "two" => 2] numbers["one"] = 5 numbers["three"] = 3 // new key:value ``` To check for a key's existence, you can access it as normal and check if it's `nil` or truthy: ```swift if user["location"] == nil // do smth end ``` ### Nil Aria has a Nil type and yes, I'm totally aware of its problems. This was a choice for simplicity, at least for the time being. In the future, I plan to experiment with optionals and hopefully integrate them into the language. ```swift let empty = nil ``` ### Type Conversion Converting between types is handled in a few ways that produce exactly the same results. The `as` operator is probably the more convenient and more expressive of the bunch. Like all type conversion methods, it can convert to `String`, `Int`, `Float` and `Array`: ```swift let nr = 10 nr as String nr as Int nr as Float nr as Array ``` Provided by the runtime are the appropriately named functions: `String()`, `Int()`, `Float()` and `Array()`. ```swift let str = String(10) let int = Int("10") let fl = Float(10) let arr = Array(10) ``` The `Type` module of the Standard Library provides interfaces to those same functions and even adds some more, like `Type.of()` and `Type.isNumber()`. ```swift let str = Type.toString(10) let int = Type.toInt("10") let fl = Type.toFloat(10) let arr = Type.toArray(10) ``` Which method you choose to use is strictly preferential and depends on your background. ### Type Checking There will be more than one occassion where you'll need to type check a variable. Aria provides a few ways to achieve that. The `is` operator is specialized in checking types and should be the one you'll want to use practically everywhere. ```swift let nr = 10 if nr is Int println("Yes, an integer") end ``` There's also the `typeof()` runtime function and `Type.of()` from the Standard Library. They essentially do the same thing, but not only they're longer to write, but return strings. The above would be equivalent to: ```swift if Type.of(nr) == "Int" println("Yes, an integer") end ``` ## Operators You can't expect to run some calculations without a good batch of operators, right? Well, Aria has a range of arithmetic, boolean and bitwise operators to match your needs. By order of precedence: ```swift Boolean: && || (AND, OR) Bitwise: & | ~ (Bitwise AND, OR, NOT) Equality: == != (Equal, Not equal) Comparison: < <= > >= Bitshift: << >> (Bitshift left and right) Arithmetic: + - * / % ** (addition, substraction, multiplication, division, modulo, power) ``` Arithmetic expressions can be safely used for Integers and Floats: ```swift 1 + 2 * 3 / 4.2 2 ** 8 3 % 2 * (5 - 3) ``` Addition can be used to concatenate Strings or combine Arrays and Dictionaries: ```swift "obi" + " " + "wan" [1, 2] + [3, 4] ["a" => 1, "b" => 2] + ["c" => 3] ``` Comparison operators can compare Integers and Float by exact value, Strings, Arrays and Dictionaries by length: ```swift 5 > 2 3.2 <= 4.5 "one" < "three" [1, 2] > [5] ["a" => 1] < ["b" => 2, "c" => 3] ``` Equality and inequality can be used for most data types. Integers, Floats and Booleans will be compared by exact value, Strings by length, Arrays by the value and position of the elements, and Dictionaries by the the combination of key and value. ```swift 1 != 4 1.0 != 2.5 true == true "one" == "three" [1, 2, 3] != [1, 2] ["a" => 1, "b" => 2] != ["a" => 5, "b" => 6] ``` Boolean operators can only be used with Boolean values, namely `true` or `false`. Other data types will not be converted to truthy values. ```swift true == true false != true ``` Bitwise and bitshift operator apply only to Integers. Float values can't be used, even those that "look" as Integers, like `1.0` or `5.0`. ```swift 10 >> 1 12 & 5 | 3 5 ~ 2 ``` ### Shorthand Assignment Operators like `+`, `-`, `*` and `/` support shorthand assignment to variables. Basically, statements like this: ```swift count = count + 1 ``` Can be expressed as: ```swift count += 1 ``` ## Functions Aria treats functions as first class, like any sane language should. It checks all the boxes: they can be passed to variables, as arguments to other functions, and as elements to data structures. They also support recursion, closures, currying, variadic parameters, you name it. ```swift let add = func x, y x + y end ``` Parantheses are optional and for simple functions like the above, I'd omit them. Calling the function needs the parantheses though: ```swift let sum = add(1335, 2) ``` ### Type Hinting Like in strong typed languages, type hinting can be a very useful feature to validate function arguments and its return type. It's extra useful for library functions that have no assurance of the data types they're going to get. This function call will produce output: ```swift let add = func (x: Int, y: Int) -> Int x + y end println(add(5, 2)) ``` This however, will cause a type missmatch runtime error: ```swift println(add(5, "two")) ``` Aria is not a strong typed language, so type hinting is completely optional. Generally, it's a good idea to use it as a validation measure. Once you enforce a certain type, you'll be sure of how the function executes. ### Default Parameters Function parameters can have default values, used when the parameters are omitted from function calls. ```swift let architecture = func bits = 6 2 ** bits end architecture() // 64 architecture(4) // 16 ``` They can be combined with type hinting and, obviously, need to be of the same declared type. ```swift let architecture = func bits: Int = 6 2 ** bits end ``` ### Return Statement Until now we haven't seen a single `return` statement. Functions are expressions, so the last line is considered its return value. In most cases, especially with small functions, you don't have to bother. However, there are scenarios with multiple return points that need to explicitly tell the interpreter. ```swift let even = func n if n % 2 == 0 return true end false end ``` The last statement doesn't need a `return`, as it's the last line and will be automatically inferred. With the `if` on the other hand, the interpreter can't understand the intention, as it's just another expression. It needs the explicit `return` to stop the other statements from being interpreted. In the case of multiple return points, I'd advise to always use `return`, no matter if it's the first or last statement. It will make for clearer intentions. ### Variadic Variadic functions take an indefinite number of parameters and merge them all into a single, Array argument. Their first use would be as a sugar: ```swift let add = func ...nums var count = 0 for n in nums count = count + n end count end add(1, 2, 3, 4, 5) // 10 ``` Even better, they can be used for functions that respond differently based on the number of arguments: ```swift let structure = func ...args if Enum.size(args) == 2 let key = args[0] let val = args[1] return [key: val] end if Enum.size(args) > 2 return args end args[0] end structure("name", "John") // dictionary structure(1, 2, 3) // array structure(5) // integer ``` Functions may have as many parameters as needed, as long the variadic argument is the last parameter: ```swift let calc = func mult, ...nums mult * Enum.reduce(nums, 0, func x, acc do x + acc end) end calc(10, 1, 2, 3, 4) // 100 ``` Variadic arguments can even have default values: ```swift let join = func (glue: String, ...words = ["hello", "there"]) String.join(words, glue) end join(" ") // "hello there" ``` ### Arrow Functions Very useful when passing short functions as arguments, arrow functions provide a very clean syntax. They're handled internally exactly like normal functions. The only difference is that they're meant as a single line of code, while normal functions can handle blocks. This normal function: ```swift let sub = func x x - 5 end ``` Is equivalent to: ```swift let sub = (x) -> x - 5 ``` They're not that useful to just spare a couple lines of code. They shine when passed as arguments: ```swift Enum.map([1, 2, 3, 4], (x) -> x * 2) Enum.reduce(1..10, 0, (x, acc) -> x + acc) ``` ### Closures Closures are functions inside functions that hold on to values from the parent and "close" them when executed. This allows for some interesting side effects, like currying: ```swift let add = func x func y x + y end end add(5)(7) // 12 ``` Some would prefer a more explicit way of calling: ```swift let add_5 = add(5) // returns a function let add_5_7 = add_5(7) // 12 ``` You could nest a virtually unlimited amount of functions inside other functions, and all of them will have the scope of the parents. ### Recursion Recursive functions calculate results by calling themselves. Although loops are probably easier to mentally visualize, recursion provides for some highly expressive and clean code. Technically, they build an intermediate stack and rewind it with the correct values in place when a finishing, non-recursive result is met. It's easier to understand them if you think of how they're executed. Let's see the classic factorial example: ```swift let fac = func n if n == 0 return 1 end n * fac(n - 1) end ``` Keep in mind that Aria doesn't provide tail call optimization, as Go still doesn't support it. That would allow for more memory efficient recursion, especially when creating large stacks. ### Tricks As first class, functions have their share of tricks. First, they can self-execute and return their result immediately: ```swift let pow_2 = func x x ** 2 end(2) ``` Not sure how useful, but they can b ... ...

近期下载者

相关文件


收藏者