enact-lang / enact Goto Github PK
View Code? Open in Web Editor NEW(WIP) Enact: A compiled programming language that's simple, familiar and fast.
Home Page: https://enact-lang.github.io
License: MIT License
(WIP) Enact: A compiled programming language that's simple, familiar and fast.
Home Page: https://enact-lang.github.io
License: MIT License
Because this language is intended to be simple, I think otherwise
keyword would be better than else
. Also, for experienced programmers, else
would be better, so it will be nice to have both else
and otherwise
.
so, in the example from README:
// FizzBuzz in Enact
each i in 1..20:
given (i % 3 == 0, i % 5 == 0):
when (true, true):
print("FizzBuzz")
when (true, false):
print("Fizz")
when (false, true):
print("Buzz")
otherwise: // <-- this looks better for beginners.
print(i)
end
end
I'd now like to implement references, as in the following:
const x = 2
// Create references with &
const ref = &x
// Dereferencing is automatic
print(ref + 3) // 5
var y = 3
// Create variable references with &var
var yref = &var y
yref = 2
// You cannot reference a reference
const refref = &&x // Error!
// ...Or an rvalue
const rvref = &2 // Error!
// Reference types: &T/&var T
fun add(a int, b int, target &var int):
target = a + b
end
// Subscripting a variable array returns a variable reference
var arr = [1, 2, 3, 4, 5]
arr[0] = 2 // arr = [2, 2, 3, 4, 5]
// Unless the array is const, of course
const carr = [1, 2, 3, 4, 5]
carr[0] = 2 // Error: Cannot assign to a const reference!
// Same applies for variable structs
struct Person:
name string
age int
end
var jim = Person("Jim", 99)
// Accessing a struct member returns a reference
jim.age = 98
// Unless the struct is const, of course
const sam = Person("Sam", 42)
sam.age = 43 // Error: Cannot assign to a const reference!
// Variable references are implicitly convertible to const references, but not the other way around
fun printPerson(person &Person):
print("Person("+person.name", "+string(person.age)+")")
end
printPerson(&var jim) // Works, but could be confusing
printPerson(&jim) // Better
fun jimmifyPerson(person &var Person):
person.name = "Jim"
person.age = 99
end
jimmifyPerson(&sam) // Cannot do this!
Please add comments in the source codes so that others can contribute to it.
Fix the issues with Travis CI introduced in #36.
The version of Clang preinstalled is too old and for some reason I am unable to install a newer version.
This is a lower-priority issue at the moment, but the REPL should be preserving the environment as it goes, e.g:
enact > const foo = 2
enact > foo (you get an "undefined" error, but you shouldn't!)
Due to the way everything works right now this isn't as simple to implement as it may seem - the bytecode chunk must be preserved and execution must be paused rather than stopped.
Not implementing generics initially was a mistake- each
loops depend on their implementation.
Arrays parse and analyse just fine, but right now, when you create or subscript an array, you get a compilation error:
[line 1] Error at '[':
[1, 2, 3, 4, 5]
^
Not implemented.
I'd now like to resolve this by implementing array literals, array subscription and array assignment in the compiler and VM.
I'd like to replace these compile-time options in common.h
:
#define DEBUG_PRINT_AST
#define DEBUG_DISASSEMBLE_CHUNK
#define DEBUG_TRACE_EXECUTION
#define DEBUG_STRESS_GC
#define DEBUG_LOG_GC
...with runtime argv flags, e.g:
enact --debug-stress-gc script.en
All of the flags that precede the script filename will be passed to the Enact interpreter, and those that supersede it will be passed to the script itself.
Perhaps a --debug-enable-all
flag too, to emulate #define DEBUG
.
The old analyser
module needs to go, particularly given the significant language changes that have occurred. I did start working on a sema
module; I intend to pick up where I left off with that.
One pass over the AST (as was conducted by analyser
) will not suffice. The minimum number of passes I think are reasonable is somewhere around 3, which is unfortunate but necessary.
impl
blocks and associate them with struct
sThis should allow for declarations in any order. I'm considering experimenting with bidirectional type checking, so that generics are a bit more elegant. Consider:
func cast(x $From) $To => x as $To;
func takesAnInt(x int) int => x;
// Without bidirectional type checking
takesAnInt(cast(2.5) as int);
// With bidirectional type checking
takesAnInt(cast(2.5));
Now that parsing and analysis are down, it's time to write a compiler and VM.
To achieve this, I'll need to design a bytecode instruction set, write a VM to run it, and then write a compiler to convert the AST to bytecode.
The following classes need to be implemented:
Value
, Object
Chunk
VM
Compiler
Before this implementation moves further, it must be able to parse all syntax described here correctly to an AST.
Issues as small as passing by value instead of reference to major design flaws in classes plague the code in this repository, and must be addressed.
This is the reference implementation of Enact, so first and foremost, it must work. But as a piece of code, it should also be correct, idiomatic and readable. More commenting needs to be done throughout the entire codebase.
Plus, it needs to run fast, without leaking memory. There's a long road ahead in terms of optimization that needs to be done.
It's certainly no easy feat, but I'm sure that over time, these issues can be gradually addressed and resolved.
The language has changed significantly, see #73. Accordingly, the lexer will need to be updated. Curly braces are now the block delimiters as opposed to :
and end
. Semicolons have been added back in, keywords have been added and changed, and string interpolation is now supported. Bitwise operators have been added.
I always wanted other languages to have it, but i have found 0 languages with this feature.
Currently, in enact there’s currently only const
,
but let me introduce you to the fixed
( or just fix
).
Basically it behaves like const before #68. Example with painting: So when the painting is fixed, you just can’t move it or put it somewhere else (assign to it), but when it’s constant, it is completely static and unchangeable, so you can’t even paint on the painting.
The syntax is the same as with const:
fixed list = [1, 2, 3]
list = [ 69, 42 ] // error
list[0] = 3 // :D
(heh this is #69)
Now that the lexer is compatible with the new syntax, the AST and the parser must also be modified. Most of the statements from the old language have survived, just with different syntax. The most notable changes are as follows:
{}
given
/when
=> switch
/case
impl
blocks instead of defining methods in type[]
, no more array literalsvar
/const
=> mut
/imm
The for
loop, as it stands, is one of the ugliest parts of the language, mainly due to the use of the semicolon:
for var i = 1; i <= 20; i = i + 1:
print(i)
end
I intend to slightly change the syntax by replacing the semicolons (;
) with vertical separators (|
) to remedy this:
for var i = 1 | i <= 20 | i = i + 1:
print(i)
end
It looks and feels more modern and Enact-y.
Now that the parser is complete, the next stage is semantic analysis/type checking. This will be handled by the Analyser
class.
Tuples need to be implemented at some stage. Here's what the syntax is looking like:
// Tuple expression
(2, "yee")
const tuple = ("Jim", 99, 131.5)
// Deconstruction
var (name, age, height) = tuple
// Indexing
tuple.0 == name
// Selective deconstruction
var (name2, _, _) = tuple
// Given deconstruction
given tuple:
when ("Jim", _, 21.01):
print("Jim is short")
when (name, age, height):
print(name+" is "+string(age)+" years old and "+string(height)+"cm high")
end
Currently, when defining a variable with const
, all that is ensured is that the variable itself is not reassigned. Properties of the variable can still be reassigned and changed - I'd like this not to be the case.
Here's an example of what I'm talking about:
const arr = [1, 2, 3, 4, 5]
// You can't do this:
arr = [1, 2]
// But you can do this:
arr[0] = 2
// This shouldn't be allowed!
// Same applies for structs:
struct Person:
name string
age int
end
const jim = Person("Jim", 29)
// You can't do this:
jim = Person("Sam", 45)
// But you can do this:
jim.name = "Sam"
// And you shouldn't be able to!
I've decided to move away from the references proposal described in #60, and instead pass Objects by reference by default.
To replace the added control that references would have added, I've decided to add a new copy
operator, which acts like this:
struct Person:
name string
age int
end
fun setAgeTo99(people [Person]) int:
each person in people:
person.age = 99
end
end
var people = [Person("Jim", 20), Person("Sam", 84), Person("Matt", 42)]
setAgeTo99(copy people)
// `people` in this scope is unaffected
setAgeTo99(people)
// `people` in this scope are now all 99
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.