projectmoon / eve2 Goto Github PK
View Code? Open in Web Editor NEWLearning experiment for designing a prototypal programming language
Learning experiment for designing a prototypal programming language
print statement prints out null for non-existant objects that do not use property resolution.
The interpreter should serialize Script
objects to disk for faster loading later. When a file is executed or imported, the interpreter should check the cache (~/.eve/cache
) for a serialized form of the requested file. If the last modified of the executing .eve file is > than the last modified of the serialized form, use the new form and re-serialize. Otherwise just use the serialized form.
As an added bonus from this, we can specify an option to ahead-of-time compile a file via this functionality. In this case, the "compiled" file is placed in the current working directory as a .evec
file. If the interpreter receives a .evec file as the script to execute, it should simply copy the file to the cache and then execute as normal.
Since UpdateVariableStatement requires a variable to have been initialized with var, InitVariableStatement should require a variable to not exist. We may be able to implement #30 with this change as well.
When a non-existent function is called, the interpreter should throw an error. However, it apparently just ignores the statement. This might be a case of unassigned expressions being thrown out, but there must be something else going on because assigned functions can be called without assigning the result to anything.
Need to implement lists and the array accessor syntax. This is composed of several parts:
Assigning a variable or property inside a function doesn't work:
def x = (a) { a = 5; print(a); }
var y = 4; x(y);
The above will print 4 inside the function. This seems to happen in both global and prototype functions.
Currently ScopeManager uses putField no matter what the scope is. In functions it should use putTempField instead.
The import function can currently import files. But every time it is called, it will execute that file. This could be bad and/or wasteful if a library is imported many times all over the place. The import call should simply return if a file has already been imported.
It would be nice to allow the import function to import more than files. If I come up with a file format for describing a namespace, we could import that and have import execute all files inside it. It would also be lovely to enable Maven repository downloading. Perhaps use Ivy for this task.
The import function should have a paths property of type list
. This property will be searched for finding places to import the specified file. It will default to the current working directory and the location of the standard library. The import function should also implicitly add .eve if a file passed to it does not have .eve on the end of it.
This could be much more difficult, but it is worth it. Given a URI like mvn://commons-cli/commons-cli/1.2
, it should download the dependency and add it to the classpath at runtimme. We can probably use Ivy's Java API for this, although I understand it is difficult to use.
Add the ability to put items into lists and dicts during declaration.
List: var l = [ 1, 2, "3", 4 ];
Dict:
var d = { key1 = "value1", key2 = 5, key3 = true };
Both of these always return true. They need to be implemented.
Prototypes can access module/global variables without using module::
or global::
. This only applies to the proto statement itself. Functions defined inside the prototype are fine. An example:
var x = 5;
proto example {
print(x);
}
This will print the global x variable. It should be `print(module::x);`` instead.
Need :: to reference specific scopes, since scope is very explicit in Eve.
proto Base {
def doSomething = () {
print("doing something in super");
}
def onClone = (new) {
new.super = self;
}
}
var sub = clone Base;
def sub.doSomething = () {
self.super.doSomething();
print("doing something in child");
}
sub.doSomething();
This example from the wiki fails with a stack overflow error because of an infinite function invocation loop. Changing the name of the child's doSomething to doSomething2 makes it work. However, sub.doSomething should become different from sub.super.doSomething in this example.
Currently the clone statement takes an identifier only. It should also allow expressions to be cloned.
Objects sent back from a return statement inside an if have no way of indicating that the function should return. Need to add a "passthrough" to send up the indication.
Currently, only numbers can be used to access list indices. This is because the [n] is actually part of the identifier. That is then parsed out and numbers found. It simply does not support anything else. The best solution to this would be to figure out how to work list indices into the ANTLR parsers. Otherwise, the quick ghetto solution would be to add support for at least variables. We will eventually have to implement them into the ANTLR parser though for real world application.
The ++
and --
operators should be implemented for int
and double
types. This would make implementing traditional for loops nicer.
The interpreter should print a stack trace when it fails.
Because of the change that disallows direct assignment of variables, this has inadvertently broken event-based inheritance. Because the onClone event is fired whenever something is cloned, this will create an infinite loop during event-based inheritance.
Problem is that it's left-to-right, so we are flattening the tree and not parsing it right. i.e. first global statement will get added to a the deepest block (function in our test case). That is bad.
Eve types boolean, string, and int need to map to both object and primitive versions of their Java counterparts for constructor searching:
Verify that loops can be returned from. Don't think this is currently the case.
var x = 1;
var y = x;
x.a = 10;
print(y.a);
When assigning a variable to a new variable, it does not clone. The interpreter should either implicitly clone, or require the use of the clone
statement. I am a fan of requiring clone
, but I would need to make sure I can detect it.
Constructors which require char
primitives currently do not work, because nothing maps to the char
type. Will have to implement mapping of one-character eve strings to both java.lang.String, java.lang.Character, and char. This will actually be fairly simple, as we just need to catch single-length strings and add three constructor possibilities:
java.create("java.lang.Character", "t")
should look for Constructor
objects with java.lang.String
or char
or java.lang.Character
as a parameter.
The interpreter does not recognize assignment of a new property as a modification to an object. Thus, adding a new property or changing an existing property of one object will also affect its clones.
Implement a with
statement to ensure variable scope. Would be used for closures in loops. This statement is not like JavaScript's with statement for aliasing variables. It is like the let
statement that makes sure variables are in a unique scope context. Proposed syntax:
def x = (a, b, c) {
var funcs = [];
for (var c = 0; c < 3; c++) {
with (a, b, c) {
def func = () {
print(a ~ b ~ c);
}
funcs[c] = func;
}
}
return funcs;
}
In the latest development branches, clone is an expression and can clone expressions. It has some issues at times that require it to have parentheses when it would seem like it wouldn't need parentheses. Need to investigate more.
Should convert print to NOT print newlines, and make a new println statement that prints newlines.
If a function returns a function, the interpreter will not recognize something like func()();
. You must assign the returned function to a variable and then call that variable.
Variables/properties should be able to have special get/set functions that act as getters and setters. This would not be that difficult to implement. Just as we check for a toString function in EveObject#toString()
, we can also check for get/set functions in getField
and putField
. The only problem is that we will have to figure out how to do a reference variable, because we do not want to disallow direct assignment in set
, but at the same time we do not want to expose pointer logic. Two proposed solutions would be to use a return
statement from set
, or create a new keyword that only works in the set function.
def X = (py) {
proto x {
var y = 10;
y.get = () {
return py;
};
}
return x;
}
var x = X(20);
print(x.y);
py
is not found in the accessor function, although it should be found.
Need to implement equals method for all EveStatements to enable function comparison.
Prototypes are basically object literals already. The only problem is that they can only be defined in global scope and are evaluated as soon as the interpreter comes across a CreateProtoStatement. If we allow prototypes to be defined anywhere, they could be used as object literals. CreateProtoStatement will also need to be modified to place the prototype in the proper scope.
For full effect, we should allow anonymous prototypes that work as expressions. That way, the syntax would be more elegant if returning a prototype from a function.
The interpreter should detect shadowing and then spit out an error. Alternatively, I could fix the interpreter to find shadowed variables properly...
For consideration: change the ::
operator to be a namespace operator, and allow the interpreter to resolve scope a little bit more loosely than it does right now. We would lose explicit scope notation though. I suppose we could make a global
namespace and implicitly add all global variables to it. That would keep the global::
operator working.
This also raises a problem about closures. If we search up the scope chain to find variables, we no longer need the closure::
operator. How do we detect closures? I suppose we can detect if a function is returned and then trigger closure scope there. But will that have any adverse effects?
Under this proposal, the new use of the ::
operator would be for namespacing. Individual eve files could use a namespace statement/function to indicate what namespace they belong to. The file can then be imported and objects from that file referenced via namespace::obj
. The internal code would not change much. An EveObject representing the namespace would be pushed on to the stack to find the variable. We may have to do it like closures and do a complete stack switch.
Namespaces would each have to have their own scope stack. The namespace function would be a native call that can only be called once, and it would create a new global.
Currently, [] is part of the identifier, and it is parsed out. This only works with numbers. Index access should be changed to a sort of unary operator that takes an expression inside the braces. This expression must evaluate to an int type. We will also need to allow multi-dimensional access (e.g. x[0][3]). The array expression evaluation should be a tree structure where [0] is evaluated before [3].
Clone an object inside a function, then assign a function as a property to the new clone. Refer to "self" inside the function. Get "self is undefined" error.
The interpreter has the ability to return functions, but they do not generate closure scope. Any reference to parent::
will be executed in the context of where ever the function gets returned. I would like to do closures, but I have to figure out how to do so. Perhaps a closure
statement to go along with the def
statement. This will make discovering closures easy.
When a closure
statement is found, the interpreter will create a new EveFunction that has a closureScope field attached to it, and the parent::
scope operator will reference that scope.
Links for Ivy and Ant are messed up.
Currently, literals are directly turned into EveObjects via WrappedPrimitiveExpression. This means that hooks and onClone events cannot pick up literal instantiation. Instead, literals should be cloned from prototypes corresponding to their data type. This will require implementation of String, Integer, Double, and Function prototypes and then changing WrappedPrimitiveExpression to clone from the proper prototype.
Literals are implicit clone statements, so if a property is attached to the proper prototype, the literal should be able to access it. None seem to work. int
literals throw a scope error. string
literals throw a syntax error.
I think it should be a syntax error to do literal.property simply because of the double data type. Parentheses around a literal should work, however. There are two root problems here:
Dots will have to become an expression somehow. It will require a massive rethinking in ScopeManager. It may make the logic simpler though.
Title says it all. Named function expressions need to be able to reference themselves via their name. The interpreter currently does not copy the name into scope.
Prototypes, despite being executable blocks of code, should not be returning anything. The return statement needs to be disallowed inside a CreateProtoStatement
.
Variables should be able to be declared via var x;
with no additional information. Getters/setters could then be attached to these. As a less obtuse syntax, the push statement should also allow initialization of variables. With a dict literal, it would make a powerful property syntax.
var x = 5;
print(x.hello()); //hello is undefined
This throws a null pointer exception error. The interpreter should stop the print statement from ever being executed because x.hello is undefined. Probably related to #18.
Similar to String/Character/char, Eve int types need to map to int.class and java.lang.Integer.class.
Due to some weird backtracking issue in the AST parser, ~ is being interpreted as == and an EqualsExpression is being created...
ASTParser blows up if an empty file is passed:
Exception in thread "main" java.lang.NullPointerException
at org.antlr.runtime.tree.BaseTreeAdaptor.isNil(BaseTreeAdaptor.java:70)
at org.antlr.runtime.tree.TreeVisitor.visit(TreeVisitor.java:54)
at org.antlr.runtime.tree.TreeFilter.downup(TreeFilter.java:115)
at eve.core.EveCore.run(Unknown Source)
at eve.core.EveCore.main(Unknown Source)
You can effectively assign variables without var. There is a problem with UpdateVariableStatement.
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.