Code Monkey home page Code Monkey logo

venusscript's Introduction

Hi! 👋

Take a look at skiley.net to see a bit of my work. Everything there was built by me (backend, frontend and infrastructure).

venusscript's People

Contributors

joaaoverona avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

Forkers

wassekaran

venusscript's Issues

Fix operator precedence

Right now, the parser can't really understand the precedence between binary operators.
For example, the sentence println(i == 5 && j == 3) is generating a compile error, because the AND operator (&&) is trying to associate the resultor 'i == 5' with the variable 'j', and not with the resultor 'j == 3'. However, the sentence println(i == 5 && !j) is valid, because there is a unary operator NOT (!) instead of a binary operator.

Implement producer-consumer as methods instead of components

For now, the features produce [someVar] and consume [someVar] are being treated as components instead of functions. And this is only because there is no variable references feature yet.
Not only this leads to a messy syntax, but also for it to work, changes were made to the parser and executor, and this is not cool at all.

Definition:

void produce(varref)
void consume(varref)

Things to note:

  • These methods will be part of StdLibrary

Example usage:

using std

export i = 0

async while true {
  sleep(1000)
  produce($i)
}

while true {
  consume($i)
  println("Produced!")
}

Implement variable references

Implement a value type of variable references. These variable references could then be used in methods, when there is a necessity of changing a variable's value, and not just a value.

Definition:

someMethod(a, b, :varRef)

Things to note:

  • Probably the character representing a variable reference will be ':' (colon).
  • In definitions, it will be impossible to accept a variable reference as argument type. For now, variable references can only be passed as argument to methods.
  • The variable reference will be a value - having it's own ValueType -, and not a Resultor.

Implement 'interpret' function

Since the language is extremely dynamic, it would be interesting to have a "interpret" function. Basically, this function receives as argument a script source code as string, and then parses it, and executes it.

Definition:

void interpret(string)

Things to note:

  • It will be necessary to save the VenusParser used to load the script, as an attribute of class Script, so the function can be able to use it.
  • We could set the return type to any, but then how to handle void functions interpreted, or any code that doesn't return nothing at all? Null is not part of the language. Maybe an interpretAndGet function?

Example usage:

using std

interpret("i = 4")
println(i)

Fix component's source line attribution

Currently, there is a bit of mess on the parser and the lexer on how the 'current line' of lexing/parsing is being calculated. Therefore, it is impacting directly on the Component's 'sourceLine' attribute, which is leading to sometimes incorrect lines in exception's messages.

Create a dialogs library

Right now, there is no UI interaction with the user. This proposal is for implementing a new library for displaying dialogs.

Definition:

bool askDialog(title, message...)
void dialog(title, message...)
void errorDialog(title, message...)
void infoDialog(title, message...)
string inputDialog(title, message...)
void warnDialog(title, message...)

Things to note:

  • Right now, there is no way to require a minimum amount of arguments to varargs functions, nor define normal types before, and then the last argument be varargs. This will make implementations a bit messy to calculate what is the title and what are the messages.

Example usage:

using dialogs

name = inputDialog("Hey there!", "Welcome to VenusScript.", "What's your name?")
infoDialog("Bye!", "See you, " + name + "!")

Implement typed function references

Currently, function references are not safe. If you pass a function reference which, for example, receives a STRING as argument, as an argument for a definition, which then calls funcReference(1), there will be no error at the start of the definition; in fact, it will only occur when executor tries to call the function, which then FunctionCall::resolve will throw an InvalidFunctionParameterException.
Since normal function calls (without references) are safe (it is: argument type validation are done before trying to call it), then this overhead of validation in FunctionCall::resolve is only because of function references being untyped.

Definition:

... def someFunction(ref**<string, int>** callback) { ...

Things to note:

  • There will be 2 new token types
  • The parser will need to read the value types at parseDefinition()
  • The actual validation of argument types will be done when passing the ReferenceValue as argument for the Definition. Probably it will be inside Definition::call.
  • VarArgs function should be always valid, no matter which types are in the reference declaration.

Example usage:

Please note that std::println is a varargs function. Therefore, the fourth note applies here.

The examples below must work:

def send(ref<string> callback) {
  callback("João")
}

send(@println)
def send(ref<any> callback) {
  callback(1)
}

send(@println)
def send(ref<int, any> callback) {
  callback("João")
}

send(@println)

The examples below must not work:

def send(ref<int> callback) {
  callback(1)
}

send(@browse)
def send(ref<string, any> callback) {
  callback("João", 1)
}

send(@browse)

Re-structure parser to always work with resultors

Right now, the current flowchart of the parser is, basically:

for token in source:
  if token is name_definition:
    if token is keyword:
      parse_keyword
    else:
      parse_attribution_or_function_call
  if token is open_brace:
    add_simple_container
  if token is close_brace:
    close_current_container
  if token is not new_line:
    exception

This way, each line must be: either a special statement (like export, include, using, for, while, ...), or a variable attribution, or a function call.
To create a simple interactive console, this gets really tricky since plain resultors in a line (e.g. 3 + 5 - 2 are invalid). There is, actually, a workaround which almost fixes this (9c9a771), but it's not working when the line starts with a name definition (e.g. a variable name or a function call). In other words: "j = 5 + 3" is valid since the flowchart already parses it, but simply "j", for printing it in interactive mode, does not works, since the flowchart would then try to parse an attribution operator or a open parenthese for a function call.

Problem: it's necessary to make the parser work with resultors directly. In practice: move variable attribution to inside readResultor(), and then try to figure out what to do with the parsed resultor. For example, create an ExecutableResultor component or something like this, and then do interactive mode checking inside the executor. Interactive mode would print the resolved resultor, while normal mode would simply resolve the resultor. In interactive mode, it would be a good idea to not change the executor's result value to the last resultor's value, otherwise the 'return' keyword would be useless.

Advantages:

  • simpler parser's flowchart
  • attributions can be embed as resultors (e.g. println(i = 3 + 5) would print 8)
  • interactive mode fully working

Disadvantages:

  • readResultor() bloated of code

Implement dynamic typed definition arguments

Right now, definitions must specify their arguments types. For example:

def print(string name, int age, any message)

Although any can be used for specifying any type as valid, it would be further better to not require any type - so it would be evaluated to any -, making it optional. For example:

def print(string name, int age, message)

Or:

def print(name, age, message)

Things to note:

  • Overloading of definitions. What will happen?
  • Container::findFunction. How to properly find functions when there is overloaded definitions, and some/one of them could contain 'any' as argument type?

Implement object types

Object types are the first - and more important - step for finally implementing object-oriented features.
Script should be able to create new object types, instantiate object types, call definitions inside object types and change attributes of object types.
Not only that, but object types can overload operators, define constructors, define attributes and create definitions.
It is not important for now to have inheritance, nor overriding polymorphism. Overloading polymorphism is already handled by the current function-finding system.

Definition:

new keyword to create a constructor
object keyword to create a new object type
opr keyword to overload an operator

Example usage:

object SomeObjectName {
  int size
  array elements
  bool enabled
  AnotherType child

  new(array elements, bool enabled) {
    this.enabled = enabled
    someFunction(elements)
  }

  opr +(SomeObjectName other) {
    return size + other.size
  }

  opr &&(SomeObjectName other) {
    return enabled && other.enabled
  }

  def someFunction(array elements) {
    this.elements = elements
    this.size = size(elements)
  }
}

Implement wait() feature

Implement a new "wait" feature. When calling this feature, the current execution workflow will be frozen until the resultor resolves into a BoolValue with value true.

Definition:

void wait(resultor)

Things to note:

  • This cannot be a function, because functions work directly with values, and this feature needs to work with resultors. Unless, of course, the function call system is reworked to allow resultor retrieving.
  • High CPU usage would happen if the resultors would be resolved every tick. As a workaround, it could be possible to use sleep() to wait for small intervals of time (like, for example, 5ms?). But then, for countdowns, or anything that checks for an exact value (equality), it could not work, since between this interval the value could have been changed more than once (see example below).
  • The executor could check for any variables being referenced by the resultor, and then add a 'listener' to the context, which would be called when the variable's value changes. For variables, it would work. But what if the resultor relies on functions?
  • If possible, implement also a version where it would wait for variable definition and not just variable attribution. For example: wait($i) would wait until the variable i is defined.

Example usages:

export COUNT = 0

async while true {
  COUNT++
}

wait(COUNT == 400)

P.S.: In this example, the second note (about high CPU usage/sleeping intervals) would not work, since on the interval the async container could have been run more than once, and then the behavior of this code would not be right.

Implement homogeneous arrays

Right now, there is no way to accumulate values into a collection. Collections are an essential feature for any imperative programming language.

Definition: (can be changed)

  • Array literal with pre-defined values:

    arrayLiteral = [ 3, 5, 7, -1, -11 ]

  • New allocated array, with no pre-defined values but defined size 25:

    newArray = []25

  • Iteration through an array:

    ... for i in arrayLiteral { ...

  • Changing an array's element at index 2:

    array[2] = 5.5

  • Retrieving an array's length if value types/attribute access is already implemented:

    array.length

  • Retrieving an array's length otherwise:

    len(array)

Things to note:

  • initially the arrays will be heterogeneous; for the future, if possible, implement them as homogeneous
  • the size of an array must be constant
  • necessary to create a new ValueType

Implement an "end script execution" feature

Right now, there is no way to totally end a script execution. This proposal is to implement a feature which lets end the script execution and all of it's created asynchronous containers.

Definition:

end

or

end()

(see below)

Things to note:

  • Should this be a component or a function? Although it seems more logical to have it as a function, have in mind that similar features, like break and continue are treated as components.

Example usage:

using std

println("Reading signals...")

while true {
  signal = readSignal()

  if signal == 0 {
    exit # OR exit()
  }
  else {
    println("Signal: " + signal)
  }
}

Implement binary and hexadecimal literals

The current lexer can only lookup tokens for decimal values. It would be a nice feature to be possible to use binary (prefix 0b) and hexadecimal (prefix 0x) literals.

Definition:

binary = 0b11111111
decimal = 255
hexadecimal = 0xFF

Things to note:

  • Octal literals are not necessary for now

Fix async and definitions variable changing

Changes to variables in parent contexts of containers which have an unique context are not being handled properly.
For getting variable values, the variable accessing works. For defining new variables, the variables are being created properly. But for changing a variable's value, it is simply being done in the context of the attribution, instead of checking which context is the owner of that variable.
While this would be the desired variable management for async containers (as stated in #4), this is not the same case for definitions.

For example:

i = 0

def someFunction() {
  println(i) # prints 0
  i = 100
  println(i) # prints 100
}

println(i) # prints 0
someFunction()
println(i) # still prints 0 instead of 100

Implement producer-consumer helper

Implement a feature which solves the producer-consumer problem. This would be a very useful feature for asynchronous/multi-threaded scripts.

Definition:

produce variable: Increases the variable value by one.
consume variable: If the variable value is higher than zero, then decreases it. Otherwise, wait until the value is higher than zero.

Things to note:

  • This cannot be a function, since the calls need to work directly with variable's and not with value's.
  • Sleeping intervals as a way to lock is NOT ACCEPTABLE.
  • Possible solution: use Java native monitors in some object (possibly even on the variable value). Problem: monitors do not have 'counting' of how many 'notify()' or 'wait()' were called. So besides the monitor, it would also need another variable for storing the amount of consumable times.

Example usage:

using std

export amount = 0

async while true {
  pushToQueue(doHttpGet("https://www.github.com")
  produce $amount
}

while true {
  consume $amount
  println(pollQueue())
}

Rework expression resolvement for object's definitions

When calling an object's definition and accessing a local variable, the variable can't be resolved. That is, because, inside InContext expression, the Context being passed by is the object's instance context, instead of the caller context.
Then, the arguments resolvement is being done within object's context instead of caller's context.
For example:

object Test {
  def test(int i) {
    println(i)
  }
}

test = new Test()
j = 2
test.test(j)

This example will fail, because there is no variable 'j' inside object's context, only in script's context.

Implement definition's 'return' keyword

Right now, functions already have 'returned value' mechanism implemented. But, still there is no 'return' keyword for user-defined functions (a.k.a. definitions) to return values.

Definition:

def someMethod() {
return someValue
}

Example usage:

using std

def sum(int a, int b) {
  return a + b
}

println(sum(3, -3))

Implement function references

Implement a value type of function references. These function references could then be stored into variables, and these variables could be 'called'.

Definition:

@functionName

Things to note:

  • The function reference will be a value - having it's own ValueType -, and a Resultor too (the Resultor holds the function name, the Value holds the function object).
  • Define a keyword to be used as it's value type. Possibilities: 'callback', 'ref', 'reference', etc.
  • Change the Container::findFunction method, to search for any variables which their values are function references.

Example usage:

def readAsyncContent(string url, ref caller) {
  async {
    content = doUrlGet(url)
    caller(content)
  }
}

readAsyncContent("www.google.com.br", @println)

Implement exported variables access

Right now, there is functionality for creating "exported" variables, which are added to the ApplicationContext instead of the local container. However, still there is no way to access these variables.

Definition:

$variableName

Things to note:

  • Changing how the lexer deals with "name definitions" to, somehow, allow using $ as a prefix for variable access, but not for variable declaration, is almost impossible now since the lexer knows nothing about which variables already exists or not. And, even for the parser, it would be a difficult task.
  • Possibly the best way is: allow $ in every name definition, but then there will be error only at run-time instead of compile-time.
  • Disallow the creation of definitions with $ in their names (the same can be applied to creation of methods), so any tentative of calling a function with $ would result in an UndefinedFunctionException.
  • Throw a run-time error when an Attribution component tries to define a variable with $ on it's name, and this variable still does not exists. This MUST be at run-time, because there is dynamic injection of exported variables/included scripts/used libraries, so there is no way, at compile-time, to really validate if a variable is defined or not.

Example usage:

using std

export count = 0

async while true {
  someHeavyTask()
  $count = $count + 1
}

while true {
  println("Tasks processed: " + $count)
  sleep(500)
}

Implement user-defined value types

Define a type which can have other attributes 'contained' on it.
In C, these are called structs. In Java, these are called classes (even though they also have methods, constructors, etc).

To be decided:

  • Should they be called 'structs', 'value types', 'classes' or what?
  • Should their attributes have a static type, or always be dynamic?
  • Which operator to use for attribute accessing. Comma?
  • Should then the language also accept constants?
    • But then, how to apply modifiers to variable declarations? i = 0 : const?

Implement debugging utilities

In a more general way, it is necessary to implement debugging utilities, like breakpoints, which could have conditions and/or dynamic injection of code. This will only be possible when #17 is implemented, and #1 is fixed, so that source code can be mapped to the generated compiled structures. Also, when implementing a small IDE in the future, it would be able to manage breakpoints easily.

Definition:

  • In Java:

Component.insertBreakpoint(String condition, String execute)

  • In a script (alternative 1):

breakpoint(string, string)

  • In a script (alternative 2):

breakpoint(any (resultor), string)

  • In a script (alternative 3):

breakpoint(any (resultor), ref (callback))

PS: the definition is subjected to changes.

Example usage:

using dialogs
using std

for i in (0, 255) {
  breakpoint("i == 155", "dialog(\"Hey!\", \"i reached 155.\")")
  # or:
  breakpoint(i == 155, "dialog(\"Hey!\", \"i reached 155.\")")
  # or:
  def breakpointDialog() {
    dialog("Hey!", "i reached 155.")
  }

  breakpoint(i == 155, @breakpointDialog)
}

Async threads are not properly throwing any runtime exception

When an exception occurs inside an async container (for example, UndefinedFunctionException), nothing happens.

Even though the executor had a queue of exceptions generated by async containers, where the containers would push the exception onto it, sometimes the main script can finish before the other async containers, and then the exception will simply be stored onto the queue and never polled.

Better modularization of async container's context

Right now, when AsyncContainers are created, they receive an unique, fresh context, which have as it's parent the context where the async is being created.
For example, the script...

i = 0
j = 5

async {
  i = 1000
}

sleep(50)
println(i)

... will print 1000, because the async container have full access to the main context, thus changes to variables will also be reflected in any other component which also access the main context.

A proposal for modularization of async container's context is defined as:

  • continue giving them an unique, new context
  • creating a copy of the current parent context, and attributing it as the new context's parent

This way, the async container can access any variables on the parent context, but their values will be calculated once (it is: at the time the async container starts). Therefore, any changes made by the async container will NOT be reflected outside it, since the parent context was COPIED and not directly referenced.

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.