charly-lang / charly Goto Github PK
View Code? Open in Web Editor NEW๐ The Charly Programming Language | Written by @KCreate
Home Page: https://charly-lang.github.io/charly/
License: MIT License
๐ The Charly Programming Language | Written by @KCreate
Home Page: https://charly-lang.github.io/charly/
License: MIT License
require("math.charly");
require("io");
require("math");
Should return a pretty printed version of the source-code representing the node.
Should the eval function be run inside the same scope as the calling stack?
Currently, array index member expressions are handled via a CallExpression
node. This makes for weird syntax that looks like this:
# Old
let myArray = [1, 2, 3];
myArray(1); # 2
# Proposal
let myArray = [1, 2, 3];
myArray[1]; # 2
Under the hood this would generate a MemberExpression node. An object can specifiy a method called __member__
that will receive all calls via this member expressions. The same goes for the assignment operator combined with a MemberExpression
class List {
func __member(index) {
# do stuff
};
func __member__save(index, value) {}
};
let myList = new(List);
myList[1]; # Executes the `__member` method
myList[1] = 2; # Executes the `__member__save` method
myList[1, 2] = 3; # Execute the `__member_save` method and passed the `2` as the second argument
This should kinda mirror the behavior of ruby.
This Issue depends on #41
The function should receive only a callback.
The AST of the block is then printed to the console via a puts
call.
The depth
property limits the depth of the printed AST.
Usage:
dump_ast(func() {
# Do stuff here
});
Variable automatically inserted into the function stack just before it is executed.
This would allow for optional parameters.
The function print
could also print multiple things instead of just one.
This depends on Issue #8
"test".reverse() # => "tset"
[1, 2, 3].map(func(e) {});
etc.
Implement some functions to make testing specific parts of a program easier.
How the API should look:
let UnitTest = require("unit-test");
let describe = UnitTest("Math", "test");
# Testing
describe("Arithmetic operations", func(it) {
it("Should add two numbers", func(assert) {
assert(2 + 2, 4);
});
});
describe("Comparison operations", func(it) {
it("Should compare two numbers", func(assert) {
assert(2 == 2, true);
});
});
describe("Failing tests", func(it) {
it("fails", func(assert) {
assert(2 == 10, true);
});
});
# Showing the result
print(UnitTest("Math", "result"));
This should print something like the following:
Math Suite
Arithmetic operations 1 of 1 tests passed!
Comparison operations 1 of 1 tests passed!
Failing tests 0 of 1 tests passed!
The lines 1 of 1 tests passed!
are replaced by the current name of the test that is being run.
We use the carriage return char to implement the rollback and redraw on each finished test.
The results should always line up
Passed suites should be colored green.
Failed suites should be colored red.
Split a string into different pieces
Returns an array where all values equal to arg are removed
Returns a new array containing copies of all values inside array
Recursively flatten an array
Returns the index of the first occurrence of value. Returns -1 if not found
Joins the elements of an array together
This would allow the following:
sleep(10.seconds());
The following should work:
class Box {
self.name = "test";
};
This allows the Interpreter to check if the main source file contains anything, before parsing the standard library.
Returns the name of the class as a string
class Person {};
let leonard = new(Person);
print(instanceof(leonard)); # "Person"
Expressions like this should be possible
1++; # 2
# a == 2
a++; # 3
(a)++; # 4
# a == 4
A postfix operator can be placed after every Term
. If the left side as an identifier the result of the postfix expression will be assigned to the identifier in the current scope.
This builds on top of this issue.
Syntax for composing different classes together to form a new one is as follows:
class Person {};
class SuperPowers {};
class TheFlash(Person, SuperPowers) {};
This just executes Person, SuperPower and TheFlash (in this order) in a shared stack.
To extend a class you write
class Person {};
class Person(Person) {};
Note that you have to be in the same scope as where the class was defined, as class definitions are block-scoped.
When doing a member expression or index expression on a non-object or non-array, the interpreter should look for a function on classes defined in primitives.charly
.
This could work like this:
class Numeric {
func times(callback) {
let i = 0;
# self automatically set to the number
# can't be overwritten (see issue #31)
while (i < self) {
callback(i);
i = i + 1;
};
};
};
"test".reverse(); # "tset"
25.times(func(i) {}); # Just like in ruby
Every class function or object function will get a pointer called self
that will always point to the current object the function runs in. In this case it will be pointed to a primitive value. The interpreter redirects these calls at runtime.
if (true && true || false) {
# This will run in this case
};
The following code:
(print)("test");
Will produce this AST:
#: Program - BT: ms
โโด#: Block - BT: 0 ms
โ โโด#: Statement - BT: 0 ms
โ โ โโด#: Expression - BT: 0 ms
โ โ โ โโด#: CallExpressionNode - BT: 0 ms
โ โ โ โ โโด#: LeftParenLiteral - (
โ โ โ โ โโด#: LeftParenLiteral - (
โ โ โ โ โโด#: ExpressionList - BT: 0 ms
โ โ โ โ โ โโด#: StringLiteral - test
โ โ โ โ โโด#: RightParenLiteral - )
โ โ โ โโด#: IdentifierLiteral - print
โ โ โ โโด#: RightParenLiteral - )
------
Initialized variables via const
can't be changed.
const a = 25;
a = 30; # This doesn't work
const b; # This doesn't work, missing initializer
const c = 25;
func asdf() {
const c = 30; # This does work
};
The prelude should only require the absolute minimum of libraries someone could ever need.
If a developer now wants to work with methods from math.charly
he should include it himself.
Should be the same in every file.
All arguments passed to the CLI after --
should be passed to the program.
ARGV will be an Array containing Strings.
A vim theme would also be nice to have.
Operator | Usage |
---|---|
Bitwise AND | a & b |
Bitwise OR | a |
Bitwise XOR | a ^ b |
Bitwise NOT | ~ a |
Left shift | a << b |
Sign-propagating right shift | a >> b |
Zero-fill right shift | a >>> b |
This is how the code should run
test(1)(2)(3)
This is how it behaves:
test(3)(2)(1);
This is most likely a bug caused by recursion in the parsing of call expressions.
If the path passed to require
points to a directory, the file called main.charly inside that directory should be included.
Reduced:
let Box = {
let children = ["test", "what"];
let iterate = children.each;
func length() {
2;
};
func __member(index) {
children[index];
};
};
Box.children.push(1);
Box.children.push(2);
Box.children.push(3);
Box.iterate(func(e) {
print(e);
});
Output:
โฏ charly test/debug.charly
test
what
function(1)(2)(3)
should work
array(1)(1)(1) = 25
should work (array syntax)
The io
module should contain methods to interact with the file system at a pretty low level.
You should be able to open streams to files, sockets etc.
The fs
module will contain high-level bindings to read and write to files, run and to receive network requests.
Each file will be a charly object containing an integer and possibly a filename. The interpreter will internally keep a mapping table between these numbers and the actual resources on the system. That way we don't have to create new primitive types just to represent system resources.
File access should be async by default (just wrap native crystal methods). (is this even possible?)
const fs = require("fs")
const net = require("net")
# Open and write to a file, closing after the block has run
fs.open("./myfile.txt", ->(file) {
file.write("hello world")
file.write("\n")
file.write("what are you doing")
})
# Read a file
fs.read("./myfile.txt", ->(content) {
print(content)
})
# Read a file without callback
fs.read("./myfile.txt") # => hello world\nwhat are you doing
# Open a HTTP server on localhost at port 3000
net.create_server("localhost", 3000, ->(req, res) {
print("Request for: " + req.path)
res.status(200).send("You are talking to charly!")
})
net
moduleThe actual definition of the net module should be included in another PR. The above example is just a basic example of how something like this could work. It should be a node.js-like wrapper around the native crystal methods.
Modules should, instead of sharing a common global scope, be able to define what values they export via the injected variable export
. The contents of this variable will be the result of the load
or require
call.
Example:
awesome-library/main.charly
class MyAwesomeClass {
# code
};
export = MyAwesomeClass;
# More code follows
main.charly
let myClass = require("awesome-library");
print(myClass); # MyAwesomeClass
Allows to write these things:
let a = 25;
a++;
a # => 26
a--;
a # => 25
Proposed new syntax to call functions
func increment(number) {
number + 1;
};
# Current syntax:
increment(25); # => 26
# Proposed syntax:
(increment 25) # => 26
(increment) # => fail (missing argument)
increment # => FunctionLiteral, won't be executed
This could always be rewritten like the following:
target op= value
becomes target = target op value
The CLI should be smarter about handling CLI arguments.
When creating a function, option to give parameters as reference or by value ๐
Semicolons should be optional.
This could be used for a couple of things
return
explicitly return a valuebreak
break out of a while loopredo
stop execution of the current block and restart at the beginning of the same blockthrow
, try/catch
catch exceptions thrown by the programmerexit
exit from the current program (should bubble all the way up to the cli)When the Parser is inside the last known production of a node, the first failing term-assumption should throw an error. Parsing should be immediately stopped and a nice little error message should show up.
Current:
2-2
- :NUM :NUM
Should be:
2-2
- :NUM :MINUS :NUM
Options:
:NOTEQ
operator as a bool-inverterImplement container syntax
container MyNewContainer {
let myName = "Leonard";
};
print(MyNewContainer.myName); # "Leonard"
Container just act like immediately initialized class expressions. There will be no constructor inside container.
Functions like length
should work for both Arrays and Strings. These functions should also work with Strings.
Callback receives a char and an index
Callback receives a char and an index
Callback receives a char and an index
Returns the first char in the string
Returns the last char in the string
Return a string in reversed order
Returns true if the string is empty
NAN
becomes Float64::NAN
let string = "123";
let real = "";
string.each(func(char1) {
real = real + char1;
string.each(func(char2) {
real = real + char2;
});
});
print("Expected: 112321233123");
print("Got: " + real);
Expected: 112321233123
Got: 112323
new(Class)
just creates a stack, executes the corresponding initialize
function and locks the stackobject.property
Proposed Syntax:
class Math {
let PI;
func initialize() {
PI = 3.14;
};
func abs(value) {
# Return the abs of value
};
};
let Math = new Math;
myVar.PI # => 3.14
myVar.abs(-5); # => 5
Implement the following functions inside math.charly
This allows for better consisentcy and future-proofing.
Allow passing a string to the interpreter-fascade instead of a filename. The CLI should handle reading the file, not the interpreter.
This could be useful for the following kind of things
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.