Coder Social home page Coder Social logo

nature-lang / nature Goto Github PK

View Code? Open in Web Editor NEW
741.0 10.0 27.0 15.13 MB

🍀 The Nature Programming Language, may you be able to experience the joy of programming.

Home Page: https://nature-lang.org

License: MIT License

CMake 0.46% C 93.69% Roff 4.14% Nemerle 1.44% Dockerfile 0.02% Assembly 0.23% Makefile 0.01%
language nature compiler programming-language hacktoberfest

nature's Introduction

nature Logo

Nature Programming Language

Nature is the modern systems programming language and compiler, striving for elegant and concise syntax while prioritizing the writing and reading experience for developers.

Key features of nature at the language level include:

  • Type system, null safety, generics, union types
  • In-house compiler/assembler/linker, not reliant on llvm. Supports compilation for amd64/riscv64/wasm architectures
  • Non-intrusive interaction with C for efficient and high-performance development
  • Progressive GC, supports both automatic and manual GC
  • Built-in vec/map/set/tup data structures
  • Package and module management
  • Function tags/closures/error prompts/runtime stack traces/coroutines
  • Integrated SSA/linear scan register allocation/reflection/assembler & linker

With the continual refinement of its standard library, nature is applicable for game engines and game development, scientific and AI computing, operating systems and IoT, and WEB development. The game engine will be the core focus for nature from versions 0.7.0 to 1.0+.

Nature is suitable for open-source authors, independent developers, as well as for learning and research. Not only do we hope you find the language convenient, but we also wish that you enjoy creating wonderful things with nature.

For more information and documentation, visit our official website:

Official website: https://nature-lang.org

Documentation: https://nature-lang.org/docs/getting-started/hello-world

❗️ Current version: 0.4.0-beta. Yet to integrate riscv64 wasm architecture compilation/manual GC/function tags/coroutines. All other features are integrated. Nature is set to release its community-friendly version (0.7.0) soon. We invite you to test it out and contribute.

⚙️ Installation

Download and unzip the nature package from releases. We recommend moving the unzipped nature folder to /usr/local/ and adding /usr/local/nature/bin to the system's environment variable.

Create a file named main.n with the following content:

import fmt

fn fib(int n):int {
    if n <= 1 {
        return n
    }
    return fib(n - 1) + fib(n - 2)
}

fmt.printf('fib result is %d', fib(30))

Compile and execute:

> nature build main.n && ./main  
fib result is 832040

Quickly compile and execute using the docker integrated environment:

docker run --rm -it -v $PWD:/app --name nature naturelang/nature:latest sh -c 'nature build main.n && ./main'  

🌱 Release Schedule

Nature's versioning adheres to Semantic Versioning. Versions 0.1 ~ 1.0 have two parts:

The first half always has a beta indication, denoting that it's not ready for production.

The second half is stable with backward-compatible syntax API. By this time, nature is suitable for personal indie/open-source projects, but LTS versions aren't provided.

With the release of version 1.0, nature will be officially used for open-source/commercial projects and will have an LTS version.

Version Content Expected Release Date
v0.1.0-beta Basic syntax release 2023-05
v0.2.0-beta Type system/Basic syntax refinement 2023-07
v0.3.0-beta Package management/Basic syntax refinement 2023-09
v0.4.0-beta Small test cases/Basic standard library 2023-11
v0.5.0-beta LSP development/Core syntax refinement 2024-02
v0.6.0-beta Medium test cases/bug fixes 2024-04
v0.7.0 Large test cases/Stable syntax API 2024-07
v0.8.0+ Preparations for the official release 2024-09
v1.0.0 Official release 2025-

Current version: 0.4.0-beta. Key functionalities still being planned will be gradually integrated in upcoming versions.

  • Integration and optimization of essential syntaxes like switch/try
  • wasm architecture compilation
  • Coroutine support
  • Compilation for the darwin system
  • Function tags support
  • Progressive GC refinement
  • riscv architecture compilation
  • Compilation for the windows system

🧭 Design Philosophy

In programming languages, there's a notion of first-class citizens. For example, in JavaScript, functions are the first-class citizens. Although in nature, functions can also be passed as values and have higher-order usages, they aren't its first-class citizens. So, what's most important for nature?

Compilation speed? Execution speed? Safety? Simplicity? None of these. Even though we aim for simplicity, we won't compromise on developer convenience.

For nature, the developer is paramount. We prioritize offering convenience, ensuring the code is aesthetically pleasing and intuitive. That's not to say nature doesn't provide fast compilation/execution, safety, simplicity, etc. We aim to balance these attributes with user-friendliness. However, when conflicts arise, the developer's convenience takes precedence.

For instance, despite most strong-typed languages opting for double quotes for standard strings, we chose single quotes to save a shift input, reducing strain on the pinky and offering a more concise read. The omission of brackets in 'if' and 'for' also stems from this principle.

Nature rarely introduces new syntactic sugar. Instead, we often choose already-existing, widely-recognized syntactic sugar from other languages, easing the learning curve and mental load for developers. Keyword abbreviations also follow popular abbreviations, e.g., i8 instead of int8_t, fn instead of func/function. fn is a common keystroke, and Rust has already popularized fn/i8, hence it drastically reduces potential misunderstandings and learning burdens.

🍺 Contribution Guide

There are many ways to contribute to nature: reporting BUGs, sharing ideas, participating in community discussions, coding, improving documentation, establishing standards, contributing resources, donations, and more.

Nature is developed based on ANSI C11 and musl libc. The codebase aims for simplicity and readability, avoiding complex third-party libraries. Filenames, directory names, and keywords all use lowercase with underscores. The only exception is macro definitions which use uppercase with underscores.

For source code directory structure, compilation, and related resources, refer to https://nature-lang.org/docs/prologue/contribution-guide.

All contributions to the standard library will eventually be merged into the main repository. Before embarking on feature development, please initiate communication via an issue for preliminary discussions and API design.

🐳 Community Interaction

For ideas and issues, we recommend discussing on Github issues so that more people can pay attention and participate.

Github Discussion Community: https://github.com/nature-lang/nature/discussions

🍼 Coding Example

Error Handling:

type test = struct {
    [i8] list
    var div = fn(int a, int b):int {
        if b == 0 {
            throw 'divisor cannot be zero'
        }
        return a / b
    }
}

var t = test {
    list = [1, 2, 3, 4, 5]
}

var (item, err) = try t.list[8]
if err.has {
    println("chain access list error=", err.msg)
}

var (_, err) = try t.div(10, 0)
if err.has {
    println("division error", err.msg)
}

Generics:

// generic fn
type numbert = gen i8|i16|i32|i64|u8|u16|u32|u64|f32|f64

fn sum(numbert a, numbert b):numbert {
    return a + b
}
fn cmp(numbert a, numbert b):bool {
    return a > b
}

// type param
type box<t> = struct {
    t width
    t length
    var area = fn(self s):t {
        return s.width * s.length
    }
}

fn run() {
    var b = box<i8> {
        width = 5,
        length = 10
    }
    println('self area=', b.area())
}

Union Types:

type nullable<t> = t|null

nullable<i8> foo = 24
if foo is null {
    // logic...
    return
}

// x println(foo + 12), foo is a union type, cannot use binary

let foo as i8
println(foo + 12)

Function Tags:

@local @retry=5 
@test 24, 10 -> 4
@test -5, 10 -> -5
fn rem(int dividend, int divisor):int {
    if divisor == 0 {
        throw 'divisor cannot be zero'
    }
    return dividend % divisor
}

@global @post increase_views
fn read_blog():int {
    // logic ...
}

@comment Based on label prompt + test for automatic code generation testing
@prompt sum up a and b
@test 12, 13 -> 25
@test -5, 10 -> 5
fn sum(int a, int b):int {}

HTTP Server:

import http
import http.router
import http.resp

var app = http.server()

router.get('/', fn(ctx):resp {
    return resp.string('hello world')
})

app.use(router).listen('127.0.0.1', 8000)

For more coding examples 👉 cases

📌 FAQ

1.Does nature use type prefix or suffix?

Nature consistently uses type prefixing, including the return type of functions. A primitive design example:

fn sum(int a, int b):int c shows that the function return type also uses type prefixing. Omitting all idents can lead to the function type declaration fn(int,int):int. Typically, the return value's ident also needs to be omitted, resulting in the formal function declaration fn sum(int a, int b):int {}.

2.What is the meaning of nature/logo?

The logo represents a spaceship, symbolizing the "Natural Selection" ship from "Three-Body". The name "nature" is derived from this.

3.Why isn't there any performance testing and comparison?

Nature is currently in its beta phase focusing on core functionality development. There hasn't been any optimization done on the compiler backend. Hence, performance testing would be unfair and meaningless.

4.How long has nature been in development?

The main repository has been under development for almost 3 years. The actual time invested is close to 6 years. What I want to emphasize is that the Nature project won't be abandoned arbitrarily and will be continuously developed and maintained with vitality.

🪶 License

This project is open-sourced software licensed under the MIT license. as a programming language, source files (.n files) and compiled binary files generated during use of Nature are not subject to Open-source license restrictions.

Copyright (c) 2020-2023 WEIWENHAO, all rights reserved.

nature's People

Contributors

chanyon avatar weiwenhao 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

nature's Issues

Add type alias?

type Fn = fn(uint) : uint
fn foo(): Fn {
  return fn(uint bar): uint {
    return bar
  }
}

creating/manipulating structs inside functions seems to blow things up

Using nature 0.4.0-beta, I was messing around with structs.

I noticed that if this standalone function argh() has the print statement with "Made it this far!" before the declaration of r2, the program compiles and runs just fine. Remove or comment out that print statement and there's a segmentation fault during the execution of the function.

Similar things seem to happen when I leave out the r2 variable entirely, and just perform arithmetic directly on the passed rectangle r.

Maybe I'm misunderstanding the care and feeding of structs but this seems odd.

type rectangle = struct {
		int width = 0
		int length = 0
		string name = ""
}

fn argh(rectangle r): rectangle {
		r.width = 3
		r.length = 4
		println("Made it this far!")

		var r2 = rectangle {
				width = r.width * 2,
				length = r.length * 2
		}
		println("Made it to here!")
		return r2
}

catch 在链式调用时不可用


var err = catch foo[0]().test.bar().baz().car()

此时 catch 无法捕捉除了 car 以外的如 null select,bar 或者 baz 中 throw 等错误。

proposal: type casts and type inference syntax

Due to the existence of the any type, type inference is necessary. However, the method similar to golang any.(Type) requires one small symbol, one large symbol, and two parentheses, which can be cumbersome. Therefore, I would like to refer to the as method in Rust and TypeScript.

Type casting

var foo = 1
var bar =  foo as bool  // bar type is bool 

Type assertion

any foo = 1
var bar = any is int // bar type is int 

One point that is quite tangled is whether the "is" syntax is necessary, or whether to use the "as" syntax uniformly, both for type assertions and type conversions. 🤔

函数返回值是元组时不使用解构语法 `Segmentation fault`

// The naturelang code is a modified version of the original Odin code from
// https://github.com/odin-lang/Odin/blob/m_aster/core/unicode/utf8/utf8.odin 
// 0000_0000 0000_0000 0000_0000 0000_0000

var RUNE_ERROR = 0xefbfbd as u32
var RUNE_SELF = 0x80
var RUNE_BOM = 0xfeff
var RUNE_EOF = ~0
var MAX_RUNE = 0x0010ffff as u32
var UTF_MAX = 4

var SURROGATE_MIN = 0xd800 as u32
var SURROGATE_MAX = 0xdfff as u32

var SURROGATE_HIGH_MAX = 0xdbff as u32
var SURROGATE_LOW_MIN = 0xdc00 as u32

var T1 = 0b00000000
var TX = 0b10000000
var T2 = 0b11000000
var T3 = 0b11100000
var T4 = 0b11110000
var T5 = 0b11111000

var MASKX = 0b00111111
var MASK2 = 0b00011111
var MASK3 = 0b00001111
var MASK4 = 0b00000111

var RUNE_MAX1 = (1 << 7 - 1) as u32
var RUNE_MAX2 = (1 << 11 - 1) as u32
var RUNE_MAX3 = (1 << 16 - 1) as u32

// The default lowest and highest continuation byte.
var LOCB = 0b10000000
var HICB = 0b10111111

type AcceptRange = struct {
  u8 lo
  u8 hi
}

arr<AcceptRange, 5> accept_ranges = [
    AcceptRange{ lo = 0x80, hi = 0xbf },
    AcceptRange{ lo = 0xa0, hi = 0xbf },
    AcceptRange{ lo = 0x80, hi = 0x9f },
    AcceptRange{ lo = 0x90, hi = 0xbf },
    AcceptRange{ lo = 0x80, hi = 0x8f }
  ]

u8 xx = 0xF1 // invalid: size 1
u8 _as= 0xF0 // ASCII: size 1
u8 s1 = 0x02 // accept 0, size 2
u8 s2 = 0x13 // accept 1, size 3
u8 s3 = 0x03 // accept 0, size 3
u8 s4 = 0x23 // accept 2, size 3
u8 s5 = 0x34 // accept 3, size 4
u8 s6 = 0x04 // accept 0, size 4
u8 s7 = 0x44 // accept 4, size 4

arr<u8, 256> accept_sizes = [
  //     1    2    3    4    5   6    7    8    9    A    B    C    D    E    F
	_as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, // 0x00-0x0F
	_as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, // 0x10-0x1F
	_as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, // 0x20-0x2F
	_as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, // 0x30-0x3F
	_as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, // 0x40-0x4F
	_as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, // 0x50-0x5F
	_as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, // 0x60-0x6F
	_as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, _as, // 0x70-0x7F
	//   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
	xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x80-0x8F
	xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x90-0x9F
	xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xA0-0xAF
	xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xB0-0xBF
	xx, xx, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xC0-0xCF
	s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xD0-0xDF
	s2, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s4, s3, s3, // 0xE0-0xEF
	s5, s6, s6, s6, s7, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx // 0xF0-0xFF
]

//unicode -> utf8 (int -> go rune)
fn encode_rune(int c): (arr<u8, 4>, int) {
  int r = c
  arr<u8, 4> buf = [0, 0, 0, 0]
  var i = r as u32
  var mask = 0x3f as u8

  if (i <= RUNE_MAX1) {
    buf[0] = r as u8
    return (buf, 1)
  }

  if (i <= RUNE_MAX2) {
    buf[0] = (0xc0 as u8) | (r >> 6) as u8
    buf[1] = (0x80 as u8) | (mask & r as u8)
    return (buf, 2)
  }

  // Invalid or Surrogate range
  if (i > MAX_RUNE || (i >= SURROGATE_MIN && i <= SURROGATE_MAX)) {
    r = 0xfffd
  }

  if (i <= RUNE_MAX3) {
    buf[0] = (0xe0 as u8) | (r >> 12) as u8
    buf[1] = (0x80 as u8) | (r >> 6) as u8
    buf[2] = (0x80 as u8) | r as u8
    return (buf, 3)
  }
  
  buf[0] = (0xf0 as u8) | (r >> 18) as u8
  buf[1] = (0x80 as u8) | (r >> 12) as u8 & mask
  buf[2] = (0x80 as u8) | (r >> 6) as u8 & mask
  buf[3] = (0x80 as u8) | (r as u8) & mask
  return (buf, 4)
}

//run ok!
// var (buf, length) = encode_rune(23456)

// run error! "Segmentation fault"
// (arr<u8, 4>, int) res= encode_rune(23456)
var res = encode_rune(23456)
var a = res[0]
println(a[0])

//struct print

数组语法分析错误

example:

arr<u8, 2> a = [ 
     0, 0, 0, 0, 0,
     0, 0, 0, 0, 0,
     0, 0, 0, 0, 0,
] // 允许最后一个`,`存在

关于类型的位置

nature变量/函数参数返回值类型位置不统一:

//nature
type Rectangle = struct {
    int length
    int width
    var area = fn(self s, Foo f):int {
        return s.length * f.width
    }
}

//参考c
type Rectangle = struct {
    int length
    int width
    int area (self s, Foo f) {
        return s.length * f.width
    }

//返回元组
  (int, string) foo () {
    return (1, "")
  }
}

//参考zig
type Rectangle = struct {
    length:int 
    width:int 
     fn area (s: self, f: Foo) int {
        return s.length * f.width
    }
//返回元组
   fn foo () (int, string) {
    return (1, "")
  }
}

//参考go
type Rectangle = struct {
   length int 
   width  int 
   fn area (s self, f Foo) int {
        return s.length * f.width
    }
//返回元组
   fn foo () (int, string) {
    return (1, "")
  }
}
//参考rust
type Rectangle = struct {
    length:int 
    width:int 
     fn area (s: self, f: Foo) -> int {
        return s.length * f.width
    }
}

在zig/rust中变量,函数参数/返回值类型位置
example:ziglang

const Rectangle = struct {
    length: usize,
    width: usize,

    pub fn area(s: *Rectangle, f: Foo) usize {
        return s.length * f.width;
    }
};

example: rustlang

struct Rectangle {
    length: usize,
    width: usize,
}
impl Rectangle {
    pub fn area(self: &self, f: Foo) -> usize {
        return self.length * f.width;
    }
}

proposal: 联合类型 union types

nullable

这是 golang 中一个示例

var foo *int // 可以将值 decode 到指针类型,因为指针类型可以表达出 nil 的含义  
foo = nil  
println(foo)  
  
var a := []int{1, 2, 3}  
a = nil  
println(a)

bar := a[0]

在 golang 中 null 可以作为一个值赋值给任意的复合类型,所以最后一行 bar := a[0] 会产生一个运行时 panic,在编译时并不能很容易的检测出这种错误。在 golang 中一个复合类型的值是不是 null 只有我们的用户自己知道,当我们明确知道一个复合类型不为 null 时,我们可以放心的编写代码。

在实际编码中,我们总是会和弱类型的语言如 mysql/json 打交道,比如使用 mysql 存储 nat 数据时,如果 nat 还没有探测出来,我们应该如何创建一条记录存储 nat 数据呢?

  • 0 🤔 No,nat=0 是 nat 允许的值,所以没有测出来不能使用 nat=0 表示
  • -1 🤔 No,nat 总是一个大于等于 0 的值,我们不能为了存储一个数据而将代码中所有的 u8 类型改成 i8 类型
  • null 😄 Yes, null 非常好的表达了值还不存在的情况。

我依旧拿我比较熟悉的 golang 进行举例,看看如何在 golang 中如何存储 nat 数据

var nat *int8 // 可以将值 decode 到指针类型,因为指针类型可以表达出 nil 的含义

// logic...
if nat == nil {
	// nil handle
}

// if foo == nil
// panic: runtime error: invalid memory address or nil pointer dereference
foo := *nat + 1 

当一个值允许为 null 时,golang 中通常使用指针加类型存储这个数据,因为指针包含了 nil 的含义。*nat 只要只要足够谨慎就完全没有问题,当你确定一个 *nat 类型的数据一定不为 null 时,你可以放心的使用 *nat 这样的操作,而不需要担心空指针引用的问题。所以其实 golang 给了用户最大的自由,让我们能够编写出足够简洁的代码。

但越来越多的强类型语言已经将 null 值作为一个特殊的值进行处理,即不允许将 null 赋值给除了 null 以为的其他类型。这样虽然增加了代码编写的复杂度(需要键入更多的字符),但是可以很大程度上避免基于 null 引用而产生的运行时错误,我想这应该是值得的。

语法支持

那 nature 中应该怎么做呢?虽然在上一个版本中 [int] list = null 是被允许的和 golang 一样。

但是我认为 *int8 不是一种合适的用来表达不存在的含义,我们应该使用更加明确的方式来表达某个值允许为 null,而不是使用 *int8 去模拟这种情况。

恰好 nature 目前还没有指针,那不妨学习一下 TS 中的实现,使用 union types 来表达一个值允许为 null。

Union types(联合类型)是一种类型系统中的概念,它允许一个值具有多个可能的类型。在许多编程语言中,包括 TypeScript 和 Python 的类型提示中,都支持联合类型。所以 union types 中虽然有多个类型,但是只有一个值。与之相对的是 Product type

nature 中使用 union type 表达 nullable,在 nature 中 null 的类型和值都适用关键字 'null' 表示

i8|null nat

// logic...

if (nat == null) {  // 这是值比较,后续将会有 nat is T 这样的类型判断语法
	// .. handle null
}

// 在明确知道不 nat 不为 null 的情况下可以使用类型断言语法 as 将 nat 的类型断言为 i8, 并作为 i8 类型使用
// 如果 nat 此时不是 int 类型,则会在运行时产生一个 panic
foo := (nat as i8) + 1

// 如果后续会频繁的使用,当然也可以这样赋值给一个变量进行使用
var n = nat as i8

union types 将作为一种标准的语法进行支持,any 其实就是一种 union 了所有类型的 union type。类似的

type numbers = int|float|uint

也将是被允许的,但是有什么用还是未知数,毕竟目前 nature 没有基于类型类型的扩展函数的语法。对于复合类型,上一个版本其还有默认值 null,下一个版本将不会再支持啦 🙇

[i8] list = null // x, null 不能赋值给 [i8] 类型
[i8] list // x, 这相当于 [i8] list = null

[i8]|null list // v, 此时 list 的值为 null
list = null // v, 允许的

string str // x,同上,这是不被允许的
string str = "" // v 这也是被允许的
string|null str // v 这是允许的

var s = str as string // v,当你明确 str 不包含 null 时,可以使用 as 语法进行断言

❗️as 同时也用于强制类型转换语法

有了 union types 就必须考虑其中的赋值操作。在没有想明白之前我们总是按照最严格的模式进行限制,严格限制意味着开放操作后总是可以兼容当前的操作。

string|null a
null|string b

a = b // x union type 不同,不允许进行相互赋值


string|null|bool a
string|null b
a = b // x
b = a // x


string|null a
string|null b
a = b // v
b = a // v

语法简化

ts 属于运行时的动态语言,所以其包含一个求值环境模型来追踪变量当前的实际类型,所以类似这样的语法是可以做到的

let foo: number|string = "hello"

console.log(foo.length); // 5

foo = 24

console.log(foo.length) // Property 'length' does not exist on type 'number'.

但是在编译形语言中,基本无法在编译时确定某一个阶段变量的值时多少,除非 foo 是一个不可变量。

int|string foo = "hello"

if (...) {
	foo = "int"
} else {
	foo = 24
}

// Is foo an int or a string?

所以我们必须能够检测出 foo 此时是什么类型才能进行具体的操作,所以这里首次引入类型判断的语法,大多数语言中使用 typeof(foo) == int 这样的判断,但是我希望能够重用类似 as 表达式基于类型的操作。我们使用 is 表达式来进行判断。

语法 bool b = foo is int ,判断 foo 的类型是否为 int 并返回一个 bool 类型的值。

int|string foo = "hello"

if (...) {
	foo = "int"
} else {
	foo = 24
}

if (foo is int && ... logic) {
	// x 虽然你已经知道了 foo 此时就是 int 类型,但是编译器此时并不知道。
	int bar = foo + 1 // x
	int bar = (foo as int) + 1 // v
	return
}

// 接下来又是一个语法糖
// 如果你已经明确知道了 foo 是 string 类型,并且后续需要频繁的操作 foo,并且不希望重新声明一个变量的名字,毕竟起名是一件很困难的事情
// 那么你可以明确的告诉编译器,后续请把 foo 当成 string 类型处理
// 其本质上等于 var foo = foo as int,但是如果你真的这么做,你会得到编译时的变量重复定义的错误
// let vs assert vs local 当然这是一个提案语法,我暂时优先选择最简短的 let
let foo as string 

string bar = foo + "bar" // v, foo 此时明确为 bar 类型

foo = null // x, 此时 foo 具有明确的 string 类型,所以不可以再将 null 值赋值给 foo 变量。

语法 let foo as string 让 foo 在当前作用域中具备明确的 string 类型。就像注释说明的一样,其本质上就是 var foo = foo as string

但是需要注意的是

type foot = struct {
	int|null bar
}

var foo = foot {
	bar = 12
}

// 不能通过这种语法来让 foo.bar 作为 int 类型
// var foo.bar = foo.bar as int 是一种不合法的语法声明方式
let foo.bar as int // x
var bar = foo.bar as int // v

nullable 简化

T|null 作为 union types 最常用的一种情况,所以通常会给予一定的语法糖进行语法简化

string|null a 可以改写成 string? a 表示 a 允许为空。很多语言都选择了这么做。

💡 因为泛形语法还在思考中,基于泛形语法也许可以进行如 type nullable<T> = T|null 类似这样的简化。所以 T? 的方式是否需要支持还需要进一步确定。至少会延期到泛形语法开发时才会确定的进行支持

error handle

使用 union type 我们同样可以进行 error 处理。nature 中函数中总是包含一个返回值,且返回值的类型是确定的,但是由于 throw 语法的存在,所以函数并不总是返回确定的类型,其可能会返回一个 errort 类型,所以现在,对于任意一次函数调用,我们可能会得到类似于 type result<T> = T|errort 这样的返回值。

但是我们应该时时刻刻的在每一次 call 时关心错误么?我觉得不需要,我们应该只关心我们能够处理的错误,对于不能处理或者预料之外的错误,我们没有必要去拦截或者处理它,应该将它继续向上传递,直到遇到一个能够处理这种错误的上级。

所以 nature 将选择一种和 golang 截然不同的更加传统的 try catch 的解决错误的方式。

fn call():int {
	// logic...call->call1->call2->call3...
	return 1
}

// call 的调用链可能非常的深,并存在了一个异常,比如有一个虫子钻进了内存中导致的内存访问异常
// 但是我只是一个小小的 caller,我能做的就是读取 call 中的数据,我无法处理类似虫子钻进了内存中导致的错误,所以只有当 call 能够返回时我才继续向下执行,否则我将不做任何的处理。
// 错误将沿着调用链向上级传递,直到遇到了一个能够处理这个错误的 caller
var foo = call()

作为一个 caller 当我能够处理可能的错误时,我将进行可能的错误的拦截,但并不是每一次调用都会产生错误,所以我需要进行适当的判断


// 通过 catch 我们可以得到一个 union type, int|errort foo
// foo 可能是其中一种类型,所以可能会有如下的写法
var foo = catch call()

if (foo is errort) {
	let foo as errort
	// log and abort to user
	log(foo.msg)
	abort(foo.msg)
	return
}

let foo as int
// normal  handle ....

但是遇到错误时,我们可能需要这么判断,那能不能进一步进行语法书写上的优化呢?

既然 union type 同一时间总是表示一种类型,在编译形语言中我们又不能向 TS 一样做类型跟踪。那不妨牺牲一点空间,将 union type 转换为 product type,从而可以减少类型断言带来的编码负担。

Product type(积类型)是指将多个类型的值组合在一起形成一个新的类型。它由多个成员组成,每个成员都有其自己的类型。可以同时获取这些成员的值。常见的 Product type 的例子是 struct 或 tuple,其中可以同时包含多个具有不同类型的字段。

var result = catch call()
var (foo, err) = 💥 result

if (err) {
	log(err.msg)
	abort(err.msg)
	return
}
var bar = foo + 1 

今天引入的语法有点多了,所以 💥 语法将暂时不会被集成到 nature 中。💥 将被集成在 catch 中。如下所示

var (foo, err) = catch call()

if (err) {
	log(err.msg)
	abort(err.msg)
	return
}
var bar = foo + 1 

文档错别字

  • 其中的 builtin_temp.n 进行了全局 import, 所以不需要进行 import 也可以访问其中的符号,入<如> errort/println/print 等都定义在该 temp 中
  • 这里有几个特殊的地方,首先为什么我们不需要通过 libc_temp.sleep() 的方式访问 sleep 呢?在 c 语言中其实没有作用域的概念,所有的符号都是全局符号,所以继承到 anture ->nature 中

(https://nature-lang.org/zh-Hans/docs/digging-deeper/temp)

Evaluate Profile-Guided Optimization (PGO) and LLVM BOLT

Hi!

Recently I checked Profile-Guided Optimization (PGO) improvements on multiple projects. The results are here. E.g. PGO results for LLVM-related tooling are here. According to the tests, PGO usually helps with the compiler and compiler-like workloads (like static analysis). That's why I think trying to optimize Nature tooling with PGO can be a good idea.

I can suggest the following action points:

  • Perform PGO benchmarks on Nature (compiler and related tooling). And if it shows improvements - add a note about possible improvements in Nature's compiler performance with PGO.
  • Providing an easier way (e.g. a build option) to build scripts with PGO can be helpful for the end-users and maintainers since they will be able to optimize Nature tooling according to their own workloads.
  • Optimize pre-built binaries

Maybe testing Post-Link Optimization techniques (like LLVM BOLT) would be interesting too (Clang and Rustc already use BOLT as an addition to PGO) but I recommend starting from the usual PGO.

Here are some examples of how PGO optimization is integrated in other projects:

By the way, does Nature compiler support compiling a program with PGO (like Clang, GCC, Rustc)? If Nature has an Ahead-of-Time compilation model, it would be a nice feature to have.

国人过来支持一下

本人文科生,只能反馈个typo:
https://nature-lang.org/zh-Hans/
主页最下方的“Discard“应为"Discord"

一门新的编程语言要成功殊为不易,需要一个社区的多年、共同努力,是N个马拉松的长跑,希望张弛有度、久久为功。

回顾那些成功的编程语言,都是借助那一波发展浪潮顺势发展起来的,往往是填补了当时其他语言的空白或与浪潮不相容的地方。
C -- 依托小型机微机上UNIX系统的流行而发展,C语言本身的设计从今天来看可商榷地方很多,但是当时是系统开发不二选的语言,累积至今的护城河更是难以逾越替代;
C++ -- 硬件软件的发展,使得软件的规模越来越大,面向对象的代码复用是软件工程化的必然要求,而且计算机的普及要求降低硬件成本,这个时候既实现面向对象、又能榨取硬件性能的C++腾空出世,到了现在虽然疆域有所缩小,语言本身变得越发臃肿,但仍处于壮年期。
Java/Javascript -- 这两种完全不同的编程语言,但其实正如名字所表明的、其起源上是有些许联系的,在发展中又都是互联网浪潮下编程语言的胜出者。Java在 server 后端,繁琐的语法看似自废武功,但正是如此才有利于水平各异、进进出出的程序员们各司其职、模块化协作构建出大型软件;Javascript在 client 前端,弱类型语法灵活,但某种程度上对于前端用户交互来说,高容错对计算机小白更为友好,毕竟小毛病问题不大不影响主要功能(开发者也省事了)。
Python -- 靠着语法简单友好和Cython成为Script胶水语言,在数值计算、人工智能这些当年还比较小众的niche领域猥琐发育了十多年。近几年,一个是人工智能的巨大浪潮,另一个是各行各业甚至文科专业越来越多的使用编程,Python 就是想不火都难……Excel都用Python了。
如今学编程、会编程的人越来越多,那么高手们怎么办?于是Rust的机会来了:-)但路还很长,还有不确定性……

希望Nature不断摸索自身的定位,笑到最后~

proposal: 泛型 generic

nature 的首个泛型版本只会支持简单的功能与严格的限制,这样才能在未来有更多的可能,而没有太多的历史负担。💪

泛型类型

其实泛型类型称为自定义类型参数更加的准确,在之前的语法中,我们已经有 type T = ... 这是和变量定义非常相似的一种自定义类型(类型别名)的语句,其原型参考自 var v = ... 。 所以实现泛类型非常的简单,只需要进一步模仿 fn f() = ...type T 声明进行优化即可,语法如下

type box<t> = struct {
	t width
	t length
}

type case<t> = (t, t, string)

type nullable<t> = t|null

type errorable<t> = t|errort

虽然类似自函数调用,但是自定义类型参数部分选择了尖括号作为参数,一方面是和函数定义能够更好的区分,另外则是大多数编程语言都使用了尖括号作为泛型参数。

在使用上则和函数调用一样


var b = box<i8> {
	width = 13,
	length = 26
}

case<u8> c = (1, 1, "hello world")

nullable<i8> foo = null
foo = 12

类型参数不支持运输条件的填写。

泛型函数

我们先来看看 golang 和 rust 中的一个简单的泛型的使用示例

golang

package main

import "fmt"

type Case[T int | uint | float32] struct {
	Width  T
	Length T
}

// 泛型函数定义
func area[T int | uint | float32](c Case[T]) T {
	return c.Width * c.Length
}

func main() {
	fcase := Case[float32]{
		Width:  1.15,
		Length: 2.15,
	}

	icase := Case[int]{
		Width:  10,
		Length: 20,
	}

	fmt.Printf("%f\n", area(fcase))
	fmt.Printf("%d\n", area(icase))
}

rust

struct Case<T> {
    width: T,
    length: T,
}

fn area<T: std::ops::Mul<Output = T> + Copy>(case: &Case<T>) -> T {
    case.width * case.length
}

fn main() {
    let fcase = Case {
        width: 1.15f32,
        length: 2.15f32,
    };

     // 在参数足够的情况下, rust 可以自主推断出类型。
     let icase: Case<i16> = Case {
        width: 10,
        length: 20,
    };

    println!("{:?}", area(&fcase));
    println!("{:?}", area(&icase));
}

泛型的设计其实基本上已经有成熟的方式,大家也都能接受这种方式,所以 nature 也将沿用前辈们的设计方案。

不过考虑到函数声明是非常频繁的操作,所以我不希望在函数声明中进一步增加语法,如泛型参数语法 <T, E> ,所以我选择简单地将泛型函数中的类型参数与约束进行提取并扩展到 type T 语法中。

// 定义了一个泛型类型 numbert, 并限定了其约束
// 💡 右值的声明虽然意思对了,但是 generic 这个单词太过复杂了,希望能够进一步精简
type numbert = generic i8|i16|i32|i64|u8|u16|u32|u64|int|uint|float|f32|f64

// numbert 是一个泛型类型参数,原则上其可以不受调用的影响而直接通过泛型约束生成所有类型的函数
fn sum(numbert a, numbert b):numbert {
	return a + b
}
// 也就是编译器将会去将上面的泛型函数泛化成下面的函数(并借助重载功能定位具体的函数)
// 重载功能将会在编译器内部被支持,但是暂时不会用户端开放
// fn sum(i8 a, i8 b): i8 {}
// fn sum(i16 a, i16 b): i16 {}

由于没有明确的使用 fn sum<numbert>(numbert a, numbert b):numbert 的方式去声明泛型函数,所以所有的类型变量都需要在函数的输入参数中明确声明,当收个类型变量,如 numbert 的值被推断出来后,后续在函数定义域内的所有的 numbert 类型变量都将沿用该值不可该变。

那能不能编写如 type t0 = generic, 此时 t0 不受到任何的约束? 🙅‍♂️ 这是不允许的。

类型约束有必要吗? 有,类型约束让我们知道这是一个强类型且具有约束的函数调用,而不是 caller 传入什么类型,callee 就泛化成什么类型。泛形函数本质上依旧是协助我们减少代码量编写的一种方式,让我不需要编写如 sumi8(),sumi16(),sumi32() ... 这样重复的工作。并且我们不能将类型当做一种可以传入的参数,如 fn box(t v),这会使得类型约束变得可有可无。

而泛型类型,如 nullable<t> 总是会传入一个确定的类型,所以不违反强类型的原则。并且类似 nullable 这种声明,t 可以是任意值,而不需要受到任何的约束,因为类型本来就是用来约束值的,所以没有必要重复的约束。相反的,类似 fn box(t v) 中的 t 部分将不再是接收调用者传递的参数来赋值,这将没有任何的意义,其总是接受一个明确的泛型类型以及相应的约束。

再多看几个泛型函数的示例

还在提案阶段没有经过实际的编码,所以还不确定复合类型变量在技术上能不能实现

type numbert = generic i8|i16|i32|i64|u8|u16|u32|u64|int|uint|float|f32|f64

// 基本使用
fn sum(numbert a, numbert b):numbert {
	return a + b
}
fn cmp(numbert a, numbert b):bool {
	return a > b
}

// 这是复合类型的情况下使用类型变量,其实和泛型类型具有相同的含义 type numbers<t> = [t]
fn list_sum([numbert] list):numbert {
	numbert sum = 0 // 使用类型变量 number
	for k,v in list {
		sum += v
	}
	return sum
}

// 当然,根据实际的情况需要,你可以优化类型参数,比如这是一个通用的求数组的长度,由于不需要关心 list_t 的具体类型,所以我们不需要像上面一样声明一个 [numbert] 的变量情况。此时 list_t 就是一个泛型类型变量。
type numberst = generic [i8]|[i16]|[i32]|[i64]...
fn list_len(numberst list):int {
	int sum = 0
	for k in list {
		sum += 1
	}
	return sum
}

// 一开始提过的 box 面积的例子,同样可以支持,这是一个通用的求盒子的面积
// 这里的 t 其实也是一个类型变量,和函数中的类型变量没有太大的区别。
type box<t> = struct {
	t width
	t length
}
// 所以我们可以有 box<int<[int]>> 这样的复杂的解析模式。都将会进行支持,并将会在编译时得到一个确定的类型
// 从这一版的定义来看,numbert 此时是一个类型变量,等着调用方赋值
fn area(box<numbert> b):numbert {
	return b.width * b.length
}

var b = box<i8> {
	width: 5,
	length: 10
}

// 类型变量 numbert 被赋值为 i8,所以此时值 a 的类型为 i8
var a = area(b)

👋👋👋

使用 `wsl2` build 失败

build

nature-lang.org/docs
cmake -B build-release -DCMAKE_TOOLCHAIN_FILE=$(pwd)/cmake/linux-amd64-toolchain.cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=Release -DCPACK_OUTPUT_FILE_PREFIX=$(pwd)/release

cmake --build build-release

/src/binary/elf/linker.c.obj:(.bss+0x58): first defined here
/usr/bin/ld: CMakeFiles/nature.dir/utils/type.c.obj:(.bss+0x60): multiple definition of `ct_symdef_size'; CMakeFiles/nature.dir/src/binary/elf/linker.c.obj:(.bss+0x60): first defined here
/usr/bin/ld: CMakeFiles/nature.dir/utils/type.c.obj:(.bss+0x68): multiple definition of `rt_rtype_ptr'; CMakeFiles/nature.dir/src/binary/elf/linker.c.obj:(.bss+0x68): first defined here
/usr/bin/ld: CMakeFiles/nature.dir/utils/type.c.obj:(.bss+0x70): multiple definition of `rt_fndef_ptr'; CMakeFiles/nature.dir/src/binary/elf/linker.c.obj:(.bss+0x70): first defined here
/usr/bin/ld: CMakeFiles/nature.dir/utils/type.c.obj:(.bss+0x78): multiple definition of `rt_symdef_ptr'; CMakeFiles/nature.dir/src/binary/elf/linker.c.obj:(.bss+0x78): first defined here
collect2: error: ld returned 1 exit status
gmake[2]: *** [CMakeFiles/nature.dir/build.make:721: nature] Error 1
gmake[2]: Leaving directory '/home/projects/nature/build-release'
gmake[1]: *** [CMakeFiles/Makefile2:221: CMakeFiles/nature.dir/all] Error 2
gmake[1]: Leaving directory '/home/projects/nature/build-release'
gmake: *** [Makefile:166: all] Error 2

Is there a version for Windows or a compilation from Linux?

Hi, do we have a compiler for Windows? Would it be possible to have? And if not, what about the cross compiler issue. In Linux, create the program and ask it to be compiled into the executable for Windows?

BUILD_OS=windows BUILD_ARCH=amd64 nature build main.n

proposal: 包管理

import 不会影响已经确定的全局符号的名称,只是提供一种能够找到 module 的方式。module 依旧是代码组织的基本模块。

将会进一步利用文件与文件夹名称阐述更多的信息,而不需要类似 package, mod 这样的声明。

sub module

如何将单个 module 分散到多个文件?

  1. user_basic.n
  2. user.basic.n

golang 在面对跨平台代码编译时采用了第一种方式,但是下划线是常用的分词方式,所以无法区分 user_basic.n 是 module 划分还是用户自定义的名称的划分。因此尝试使用第二种方式。

user.n 和 user.[sub].n 都会被视为 user module,被编译到一个文件中。

但是当使用 import file 的方式引入 module 时,并不能解锁 sub module 的功能。

import "user.n" 仅会触发对 user.n 的 import 而不包含 user.[sub].n

并且通过 import file 的方式,由于 path 仅支持以当前文件作为根路径向下寻找 module, 所以无法支持如

├── main.n
├── post
│   └── post.n
├── user
│   └── user.n
└── utils
    └── foo.n

这种情况下 main.n 可以通过 import "post/post.n" 或者 import "utils/foo.n" 引入 post 和 foo 模块,但是在 post.n 中相邻的 user 和 utils 模块是不可见的。除非能够支持 import "../utils/foo.n" ,不过并没有计划支持这样的 import file 方式。所以接下来必须借助包管理工具实现了。

package

在项目的根目录下需要有一个配置文件,暂定 package.toml

# 基础信息
name = "hello"
version = "0.1.0"
authors = ["Alice <[email protected]>", "Bob <[email protected]>"]
description = "Your package description"
license = "MIT"
type = "lib" # lib or bin
path = "hello.n" # 自定义入口文件

[dependencies]
rand = {  type = "git", version = "1.0", url = "https://github.com/foo/rand" }
seed = {  type = "local", version = "1.1.0", path = "~/Code/custom.1" }
  • version 使用的是精确的版本,尤其是在 git 时,其作为 branch/tag 进行包的拉取。
  • url 中的名字可以与包名不同,甚至完全不包含与包名相关的符号。
  • 原则上 dependencies key 需要和 package.name 相同,但是考虑到存在两个同名的包做不同的功能,所以 dependencies key 充当了 alias 的功能。当然,默认情况下推荐使用相同的名称。

当前版本暂时不去支持约束范围,仅需要支持精确版本。

package.toml 就放在项目的根目录下,执行 nature build 时如何能够找到 package.json 呢?

严格要求执行 shell 所在的目录,也就是工作目录必须存在 package.toml 所在目录。

既然已经有了这个基础限制,那么即使没有开启 package 时也应该遵循这个规范。但是不做强制检测。如 nature build src/main.n 是被允许的,这样可以在一个项目中构建多个二进制文件。在没有 package.toml 的情况下,main.n 所在到目录将会被识别为工作目录。

没有 package.toml 时,虽然 nature build 会继续执行编译命令,但是会退化成基于 import file 的模式,在非 package 模式下,一旦引用层级中出现了 import package 的方式,应该立即抛出异常。

where to put?

对于比如 url = http://test.com/foo/rand golang 中进行多级的拆分,且文件夹中包含了 @ 符号,从而可以判断出我是否加载过这个 package。 nature 则直接将 / 转换成 . 并携带上版本符号,而 local 则不需要 copy 到 sources 文件夹,每一次都去 local path 进行 import 即可,示例:

.nature
└── package
    ├── caches
    │   └── [email protected]
    │       ├── [email protected]
    │       ├── [email protected]
    │       ├── [email protected]
    │       └── [email protected]
    └── sources
        └── [email protected]
            ├── other.n
            ├── package.toml # name = rand
            ├── rand.n
            ├── rand.sub.n
            └── utils
                ├── pool.n
                ├── seed.n
                └── seed.sub.n

caches 机制留到下一个版本再去实现。

import file

语法依旧为 import "test.n" 或者 import "foo/test.n"

因为一个 file 可以被多个其他 file import, 所以 file 自身应该具有一个唯一前置标识。

上一个版本中 import file 存在 bug,是关于如何确定 import file 编译时符号唯一名称的 bug,当工作目录不在入口文件目录时会触发 import 异常。

当启用了 package.toml 时,应该以 package.toml 中的 name + version 作为入口。

当没有启用 package.toml 时,如 nature build main.n,main.n 具有入口的属性,且 main.n 只能 import 低于自身等级的 module。 所以可以用 main.n 所在目录作为入口。但是确定了入口又有什么用呢?

假设入口 = hello,首先 import 是一种寻找 module 的方式,不会影响 module 的唯一标识,通过 import 可以定位到 module 的 full path。具体的示例如下

without package.toml

test.n = /home/weiwenhao/code/nature-test/foo/test.n
main.n = /home/weiwenhao/code/nature-test/main.n

则入口为 main. 所在目录,即 nature-test,可以将 test.n 相对截取出 foo/test.n ,去掉 n 并将斜杠替换成 ., 再将入口添加进去可以得到一个全局唯一符号 nature-test.foo.test,该符号将作为一个前缀,所有的子符号都会携带上该前缀。

compiler 并不会对项目中的所有 module 都进行编译,仅当 import 时才会触发编译。

如果同时有 import "test.n"import "foo/test.n" 时,如何避免重复编译并且得到这个 module 的 unique_ident,进行引用改写呢? 可以使用 hash table 存储编译数据,使用 full path 作为 key。这样只要能够得到 full path 就行了。

exists package.toml

适用于 lib/bin 类型的 package, 在 lib package 中,同样允许使用 import path 的方式引入 module。

test.n = /home/weiwenhao/code/nature-test/foo/test.n
main.n = /home/weiwenhao/code/nature-test/main.n
package.toml = /home/weiwenhao/code/nature-test/package.toml
name=test
version=0.1.0

此时 package 的入口应该是携带上版本的 [email protected]

main.n 中包含 import "foo/test.n" 通过 import 可以找到 test.n 的 full path,进行 module 的编译,此时的 unique ident 应该为 [email protected]


所以 package 的入口关系到了 module 的 unique ident 的生成。后续的 import package 将会是一种崭新的 import 方式,但是再次重复, import 只是一种寻找包的 full path 方式,并不会影响 module unique ident 的生成。

import package

即使使用上面的示例

[email protected]
├── package.toml
├── rand.n
├── rand.sub.n
├── other.n
└── utils
    ├── pool.n
    ├── seed.sub.n
    └── seed.n

package.toml 中 name = rand。

rand.n 文件的内容大致如下

import "utils/seed.n"
// 这里的 rand 是 package.name,其能够作为起点,找到当前 package 下的所有 module
import rand.utils.pool
import rand.other

fn interger():int {
}

现在如果有一个 bin 类型的项目,通过 package.toml 安装依赖 1.0 版本的 rand, 也就是 package 处给的示例。那在 bin 的入口 main.n 中如何使用这个 package 呢? 示例

import rand // 这里 import 了 rand package,并且默认导向 rand.n module, 其相当于 rand.rand

var i = rand.interger() // v 通过这种方式可以调用 rand module 中的全局函数/变量

var b = rand.utils.seed.set() // x 不能通过这种方式引入 package 中的其他 module

import package 时不需要再使用双引号啦,且使用点语法进行 module 的引入,从而和 import file 进行区分。如果想要 import rand package 中的其他 module 则可以

import rand.other // v
import rand.rand // v
import rand.utils.seed // v, 这里包含 seed.n 和 seed.sub.n 构成的一个 seed module
import rand.utils.pool // v 

compiler

上面已经约定好了 module unique ident, 在使用时,比如 main.n 包含如下内容

import rand.utils.seed

var b = seed.set()

在检测到 seed 存在于 import 中,应该按照改写约定,将 seed.set 改写成全局唯一符号。也就是 [email protected] 虽然有很多的点,但是要记住,这是一个字符串类型的符号。

这里改写的名称应该是在全局符号表中能够找到的名称,存在一种可能,比如 dependencies key 是 foo,所以 import 是 foo, 但是实际上 package.name 是 bar, 此时应该要识别出这种情况,改写时应该改写成 package.name = bar,因为其在全局符号表中构建的 ident 为 bar

Assert (false) failed: output dir='fib' not created

# ls
fib.n


# tree
.
└── fib.n

0 directories, 1 file


# cat fib.n 
import fmt

fn fib(int n):int {
    if n <= 1 {
        return n
    }
    return fib(n - 1) + fib(n - 2)
}

fmt.printf('fib result is %d', fib(30))


# /usr/local/nature/bin/nature build -o fib fib.n 
Assert (false) failed: output dir='fib' not created [main.c:36 (cmd_entry)]
Aborted (core dumped)

# gcc --version
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

# /usr/local/nature/bin/nature --version
nature v0.4.0-beta - release build 2023-09-21
# 

Some suggestions

  1. don't use emoji in your code, you can't type this kind of thing directly on your keyboard, you have to resort to third party software, which is inefficient and doesn't make coding enjoyable.
  2. hopefully it will support windows.
    Thanks!

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.