Giter Site home page Giter Site logo

phel-lang / phel-lang Goto Github PK

View Code? Open in Web Editor NEW
396.0 12.0 19.0 4.24 MB

Phel is a functional programming language that transpiles to PHP. A Lisp dialect inspired by Clojure and Janet.

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

License: MIT License

PHP 99.93% Dockerfile 0.05% Shell 0.02%
php lisp phel language programming-language transpiler functional-programming clojure phel-lang

phel-lang's Introduction

Phel logo

GitHub Build Status Scrutinizer Code Quality Scrutinizer Code Coverage Psalm Type-coverage Status Gitter


Phel is a functional programming language that transpiles to PHP.

It is a dialect of Lisp inspired by Clojure and Janet.

Example

# Define a namespace
(ns my\example)

# Define a variable with name "my-name" and value "world"
(def my-name "world")

# Define a function with name "print-name" and one argument "your-name"
(defn print-name [your-name]
  (print "hello" your-name))

# Call the function
(print-name my-name)

Documentation

The documentation for Phel can be found on the Phel's website: https://phel-lang.org.

Community

Feel free to ask questions and join discussions on the Phel Gitter channel.

Contribute

Please refer to CONTRIBUTING.md for information on how to contribute to Phel.

phel-lang's People

Contributors

antonio-gg-dev avatar aszenz avatar cgrabenstein avatar chemaclass avatar dependabot[bot] avatar dominikb avatar drupol avatar jenshaase avatar jesusvalera avatar lhsazevedo avatar mabasic avatar p810 avatar paullaffitte avatar peeley 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  avatar  avatar

phel-lang's Issues

Undefined index: args

Hay, exicited to explore this language. I've followed the instruction on setup page and got error Undefined index: args` it just won't work

then I tried the repl with (defun create-a-string [string] (print (concat 'Printing: ' string)))

>>> (defun create-a-string [string] (print (concat 'Printing: ' string)))
Phel\Exceptions\PhelCodeException: Can not resolve symbol defun
in /home/tamibam/code/php/phel/hello/vendor/phel/phel/src/php/Exceptions/PhelCodeException.php:18 (gen: /home/tamibam/code/php/phel/hello/vendor/phel/phel/src/php/Exceptions/PhelCodeException.php:18)

PHP Notice:  Undefined index: args in /home/tamibam/code/php/phel/hello/vendor/phel/phel/src/php/Exceptions/TextExceptionPrinter.php on
line 108
TypeError: Argument 1 passed to Phel\Exceptions\TextExceptionPrinter::buildPhpArgsString() must be of the type array, null given, called in /home/tamibam/code/php/phel/hello/vendor/phel/phel/src/php/Exceptions/TextExceptionPrinter.php on line 108
in /home/tamibam/code/php/phel/hello/vendor/phel/phel/src/php/Exceptions/TextExceptionPrinter.php:148 (gen: /home/tamibam/code/php/phel/hello/vendor/phel/phel/src/php/Exceptions/TextExceptionPrinter.php:148)

PHP Notice:  Undefined index: args in /home/tamibam/code/php/phel/hello/vendor/phel/phel/src/php/Exceptions/TextExceptionPrinter.php on
line 108
PHP Fatal error:  Uncaught TypeError: Argument 1 passed to Phel\Exceptions\TextExceptionPrinter::buildPhpArgsString() must be of the type array, null given, called in /home/tamibam/code/php/phel/hello/vendor/phel/phel/src/php/Exceptions/TextExceptionPrinter.php on line 108 and defined in /home/tamibam/code/php/phel/hello/vendor/phel/phel/src/php/Exceptions/TextExceptionPrinter.php:148
Stack trace:
#0 /home/tamibam/code/php/phel/hello/vendor/phel/phel/src/php/Exceptions/TextExceptionPrinter.php(108): Phel\Exceptions\TextExceptionPrinter->buildPhpArgsString()
#1 /home/tamibam/code/php/phel/hello/vendor/phel/phel/src/php/Runtime.php(108): Phel\Exceptions\TextExceptionPrinter->printStackTrace()
#2 [internal function]: Phel\Runtime->exceptionHandler()
#3 {main}
  thrown in /home/tamibam/code/php/phel/hello/vendor/phel/phel/src/php/Exceptions/TextExceptionPrinter.php on line 148

Refactor Runtime class

Runtime has a constructor and is also a Singleton. It should only have one possibility.

Proposal: Autogenerated Runtime

This issue is a proposal for a autogenerated Runtime class that make its much easier to get started with Phel.

Motivation

Currently the Runtime needs to be configured manually. All paths of all libraries need to be added to the configuration. This can be a frustrating job when the amount of library increases or the libraries change their paths.

Concept

To resolve this problem, a Runtime configuration should be automatically generated from the installed Composer plugins. This is already done by Composer its self for PHP's autoloading standards (PSR-4, PSR-0, etc.). Composer offers a Plugin System for exactly such a purpose. (https://getcomposer.org/doc/articles/custom-installers.md)

High level view: Whenever Composer updates or installs a dependencies a new Runtime configuration file is written and saved to vendor/PhelRuntime.php. The file looks like this

<?php

// @generated by Phel
use Phel\Runtime;

require __DIR__ .'/autoload.php';

$rt = Runtime::initialize();
$rt->addPath("hello-world\\", [__DIR__ . '/../src/']);
$rt->addPath("phel\\", [__DIR__ . '/phel/phel/src/phel/']);
// More addPath

$rt->loadNs("phel\\core");
return $rt;

Given this file, the index.php (to run the application) can that be changed to

<?php

$rt = require __DIR__ .'/../vendor/PhelRuntime.php';
$rt->loadNs('hello-world\boot'); // Load the namespace to start the application

Additionally, it is now much easier to make a run command for the phel cli.

./vendor/bin/phel run /path/to/file.phel
# or
./vendor/bin/phel run hello-world\my-namespace

Requirements

In order to archive this, each library needs to provide a configuration with a namespace. I will suggest that we will put this configuration in the composer.json file under extra.phel.

{
    "extra": {
        "phel": {
            "loader": {
                "phel\\": "src/phel/"
            },
            "loader-dev": {
                "phel\\": "tests/phel/"
            }
        }
    }
}

Putting Phel's configuration in the composer.json file has the advantage that looking for all library configuration is quite easy, because all package configurations are collected into composer.lock. I choosed the extra section of composer, because it is the only section in which composer allows custom configurations (see https://getcomposer.org/schema.json).

Alternative

An alternative would be to put the configuration into a extra file, called phel.json. This has the advantage the we are independent of the composer specs. The drawback is that have to collect the phel.json files manually. Also, it may be required that we need to read the composer.json file from each Phel package anyway.

Drawbacks

To make it work, we need to change/add the following:

  • In Phel's composer.json we need to change "type": "library" to "type": "composer-plugin".
  • A requirement needs to be added: composer-plugin-api

Documentation

  • A documentation for the "phel" configuration section needs to be written
  • The Getting Started Guide must be changed
  • A documentation for Library-Developers would be great.

Improve 'Comparison operation' documentation

From my point of view, the Comparison operation section is not clear enough.

<= Check if all given values are in a non-descending order. Returns a boolean.
<  Check if all given values are in ascending order. Returns a boolean.
>= Check if all given values are in non-ascending order. Returns a boolean.
>  Check if all given values are in descending order. Returns a boolean.

Investigate if something like that is more understandable.

<= Check if all given values are in ascending order (repeat values are not allowed). Returns a boolean.
<  Check if all given values are in ascending order (repeat values are allowed). Returns a boolean.
>= Check if all given values are in descending order (repeat values are not allowed). Returns a boolean.
>  Check if all given values are in descending order (repeat values are allowed). Returns a boolean.

Bug: Comparaison with 1

Observation

Comparing anything truthy with 1 will produce true as a result, but if compared with any other number it will produce false.

Examples

All those lines will output true

(print (= 1 @["foo"]))
(print (= 1 ["foo"]))
(print (= 1 :a))
(print (= 1 @{:a "foo"}))

But those will output false

(print (= 1 nil))
(print (= :a @["foo"]))

The last one is the most surprising since 1 == :a and 1 == @["foo"], so we could expect that :a = @["foo"]

Expected behavior

We could expect that if something has a different type than something else, they cannot be equals. Or than every truthy numbers equals truthy values.

Moreover, we could expect that the equality respects the basic properties of mathematical equality (substitution, reflexive, symmetric and transitive properties). With the last example ((= :a @["foo"])) we can see that the transitive property is not respected.

Website Improvements

  • Favicon
  • 404 Error Page
  • htaccess Error Document
  • Meta description
  • Meta Keywords

Getting started - The namespace is not valid

While going though the documentation I've encountered this error right at the start:

The namespace is not valid. A valid namespace name starts with a letter or underscore, followed by any number of letters, numbers, or underscores. Elements are splitted by a backslash.
in /home/mabasic/code/src/test/hello-world/src/boot.phel:1

1| (ns hello-world\boot)
       ^^^^^^^^^^^^^^^^

When I change in file src/boot.phel this line (ns hello-world\boot) to (ns hello_world\boot), the error goes away, but I am assuming that there is something wrong here..

Are the docs outdated or is there an error in namespace resolution?

If it is the docs, I can submit a PR if you want.

:use in namespace does nothing

This code runs correctly:

(ns hello-world\boot)

(def app (php/new \Laravel\Lumen\Application "/home/mabasic/code/src/test/hello-world/"))

(php/-> app (run))

This code also runs correctly:

(ns hello-world\boot
    (:use \Laravel\Lumen\Application))

(def app (php/new \Laravel\Lumen\Application "/home/mabasic/code/src/test/hello-world/"))

(php/-> app (run))

What is the purpose of (:use \Laravel\Lumen\Application) if the file runs either way?

I was expected to get an exception or error if the php class was not included with :use in the file.

Update: Does (:use \Laravel\Lumen\Application) mean that I can reference that class by just using Application. If so, how can I alias it to something else? Also, how do I use a imported phel library? Did I miss that in the documentation?

Proposal: Renaming Phel class

Context

Let's consider this code:

// namespace: Phel
// class: Analyzer

/** @param Phel|scalar|null $x */
public function analyze($x, NodeEnvironment $env): Node
{
    if ($this->isLiteral($x)) {
        return new AnalyzeLiteral;
    }
    if ($x instanceof Symbol) {
        return new AnalyzeSymbol;
    }
    if ($x instanceof Tuple && $x->isUsingBracket()) {
        return new AnalyzeBracketTuple;
    }
    if ($x instanceof PhelArray) {
        return new AnalyzeArray;
    }
    if ($x instanceof Table) {
        return new AnalyzeTable;
    }
    if ($x instanceof Tuple) {
        return new AnalyzeTuple;
    }
    throw new AnalyzerException('Unhandled type: ' . var_export($x, true));
}

When I read this code I think:
<<<

  • The namespace Phel it makes sense because it might be considered the root of the project named Phel.
  • The class name Analyzer is also straight forward.
  • The methods analyzeInEmptyEnv() and analyze() are also easy to understand

But wait for a second, what type is $x again? /** @param Phel|scalar|null $x */

<<<

$x can be null, any scalar AND a Phel type? What does Phel in this context mean?

These are the "Phel's children":

  • Symbol
  • Tuple
  • PhelArray
  • Table

The Phel class

<?php
namespace Phel\Lang;

abstract class Phel implements IMeta, ISourceLocation
{
    use TSourceLocation;
    use TMeta;

    /** Check if the value is truthy */
    abstract public function isTruthy(): bool;

    /** Computes a hash of the object */
    abstract public function hash(): string;

    /** @param mixed $other The other value. */
    abstract public function equals($other): bool;
}

The question

What does "Phel" as an abstract class mean?
Why am I saying this? Because it seems a bit confusing, especially the first time when you look at this type Phel (knowing as well that the project/language name is Phel too).

Proposal

Wouldn't it be better to rename this (abstract) class to something more accurate to its meaning?
What about renaming Phel to DataType, for example?
Any other suggestion is more than welcome :)

Create NodeEnvironmentInterface

Do some research and look if it makes sense to create a NodeEnvironmentInterface. I think it does, but we should double-check that.

In case we want to add that interface: look for every place where it depends on the NodeEnvironment and invert it by depending on a NodeEnvironmentInterface instead.

Acceptance Criteria:

  • The place to create that interface might be for now the same where the NodeEnvironment is.
  • The NodeEnvironmentInterface must contain the public method that is truly part of the public API of the NodeEnvironment.

You can start by defining all of the public used functions from the concrete class and figure out if they make sense as part of the interface or not. (usually yes; sometimes/rarely nope).

Improve destructing

Destructing is currently only working for tuples. It should also work for Arrays and Tables. Example:

(defn [@[1 first-element 2 second-element]]
  [first-element second-element])
(defn [@[:k1 element-of-k1 :k2 element-of-k2]]
  [element-of-k1 element-of-k2])

Bug: Reduce cannot be used on a set whereas map can

Description

Using reduce or reduce2 on a set will output the following error: Cannot use object of type Phel\Lang\Set as array. But using map on a set is perfectly fine and work as expected. This is because the function first is used in the implementation of reduce
, which is used in the implementation of reduce2.

For reduce2 there is another catch, a set cannot be destructured becase the syntax [x & xs] is only supported by arrays. Here it could make sense to make this feature available for Iterators since it doesn't say that it will take the first element, it only says that it will take one element (Iterator::current) and the rest of it (Iterator::next).

Expected behavior

Since a set is an unordered data structure, it make sense that we cannot use first on it. So I think to solution is to make reduce Iterator-compatible by removing this function from its implementation and allowing this syntax [x & xs] for Iterators.

Namespace conflict when running tests

  • Possible documentation mistake in testing and configuration?

Given this config in composer.json:

"extra": {
        "phel": {
            "loader": {
                "hello-world\\": "src/"
            },
            "loader-dev": {
                "hello-world\\": "tests/"
            },
            "tests": [
                "tests/"
            ]
        }
    },

And these files:

src/boot.phel:

(ns hello-world\boot)

(println "Hello, World!")

tests/boot.phel

(ns hello-world\boot
    (:require phel\test :refer [deftest is]))

(deftest test-variable
    (is (= 4 (+ 2 2))))

When running vendor/bin/phel test, I get:

Hello, World!

Passed: 0
Failed: 0
Error: 0
Total: 0

but when I rename the test file to test.phel (and update the namespace to match) or anything other than what a file is called in src directory it works.

.



Passed: 1
Failed: 0
Error: 0
Total: 1

As far as I understand we are telling people in the documentation to use hello-world namespace for both loader and loader-dev. That is why this is happening. I'm going to send a PR to change loader-dev namespace in the documentation to hello-world\\tests\\.

Proposition: Remplace "scalar" keyword by the scalar types on PhpDocs

TL;DR: I proposed to replace all scalar keywords to string|float|int|bool. ⚠️

🔗 Description

As the PHP documentation says, there are four scalar types in PHP:

  • boolean
  • integer
  • float
  • string

Also, there is a is_scalar() method, but the scalar type doesn't exist per see. 🎯


Example 1 🌜

I attach one picture of PHPStorm from this project which the scalar keyword appears highlight, which means it doesn't exist, and bottom the same method with the "correct" implementation.

Selection_157

Example 2 🌛

You can see in this second picture the PhpDoc returns an AbstractType, null and scalar, the method itself (L115) returns a string (but string is not a "scalar"), the program works as expected, but sometimes you see this kind of IDE "warnings/error/whatever".

Selection_158

What do you think? 💯


Pros:

  • IDE help
  • No annoying messages

Cons:

  • PhpDoc too long (which is not a real problem in my opinion)

Outrageous integration with laravel for fun and profit

Hi, I love the project, I'm having a lot of fun tinkering with it, thanks a lot.


Right now, I'm looking at how deep I can go trying to use phel as laravel controllers.
Why ? Just because it's fun.

Getting it to run was really easy, just add the package to composer and install, slap

$this->app->singleton(Runtime::class, function ($app) {
    $runtime = Runtime::initialize();
    $runtime->addPath('app\\', [app_path()]);
    $runtime->loadNs('phel\core');
    return $runtime;
});

into AppServiceProvider->register, add a route macro for convenience

$router = $this->app['router'];
$router::macro('phel', function ($path, $controller) use ($router) {
    return $router->get($path, function (Runtime $runtime) use ($controller) {
        $runtime->loadNs("app\\Http\\Controllers\\$controller");
    });
});

into AppServiceProvider->boot, and now I can match urls to phel script files with

Route::phel('/foo', 'bar'); // matches "example.com/foo" to `App\Http\Controllers\bar.phel`
(ns app\Http\Controllers\bar)

(print "Phel sends his regards.")

And it works... Kinda.


I have one issue and a few questions or suggestions

ERROR : output contains some error message

Phel sends his regards.<p>Unterminatend list<br/>in <em>C:\laragon\www\laraphel\app\Http\Controllers\bar.phel:4</em></p><pre><code>4|

</pre></code>

Even if I remove (print "Phel sends his regards."), I still get this error message.

Current implementation uses php's exit liberally

This prevents any one to do anything after phel has run, or even using ob_ functions to work with output.

Is there any way to pass a few global php variables to phel scripts ?

Something like $runtime->loadNs('phel\core', ['request' => $request, ...])

I was able to get Laravel app/container and assign it in phel, as it's a singleton, but it can be quite verbose quite fast.

(ns app\boot
  (:use \Illuminate\Foundation\Application))

(def app (php/:: Application (getInstance)))

(defn resolve [binding]
  (php/-> app (make binding)))

Is there a way to call global php functions in phel ?

I don't see anything in the docs on that front, it only works with object or classes as far as I can tell.

I wanted to convert view('welcome') in php to something akin to (php\call view "welcome") in Phel. So far I have to redefine it in Phel

# I'm using my previously defined resolve function
(defn view [viewfile]
  (php/-> (resolve "view") (make viewfile)))

Is there any way to return something from phel to php ?

Even if phel didn't "hard exit" the script, I don't know how I can pass something back to the php side of things... loadNs return a boolean, and ob_ functions can't really deal with anything more than output.

Bug: Core tests are warning about something wrong

Bug

These tests (in tests/core.phel) are displaying an NOTICE error:

(assert (= (set 1 2 3) (push (set 1 2) 3)) "set push")
(assert (= (set 1 2) (push (set 1 2) 2)) "set push existing value")

Output:

Notice: Only variables should be passed by reference in /private/var/folders/qq/v42dxsw95cv30dt79r7b2dbr0000gn/T/__pheloqcXU6 on line 4
PHP Notice:  Only variables should be passed by reference in /private/var/folders/qq/v42dxsw95cv30dt79r7b2dbr0000gn/T/__pheloqcXU6 on line 4

How can I reproduce it?

You can verify it by commenting these lines out (by adding # at the very beginning of the line) and running the tests again (using composer test-core); that NOTICE will be gone.

I think it might not be related to the sets but with the push... This needs some investigation.

Implement private functions and values

  • def- ...
  • defn- ...
  • def name @{:private true} ...
  • defn name @{:private true} ...

When functions or values are private they should not be accessible when imported to another file.

Refactoring Printer

The current Phel\Printer class is a "God Class" with too many responsibilities. Actually 19, one per each conditional inside the print() function. See:

public function print($form): string
{
    if ($form instanceof Tuple) {
        return $this->printTuple($form);
    }
    if ($form instanceof Keyword) {
        return ':' . $form->getName();
    }
    if ($form instanceof Symbol) {
        return $form->getName();
    }
   // ... etc

Suggestion 1: Using extract class refactoring

  • Create a new Module named Printer and there allocate each private function in a class. This way it will be easily testable.
  • A similar strategy as we already did for the Analyzer module.

Suggestion2: Using polymorphism

  • Instead of having all information about how to print anything inside this GodClass, consider moving the responsibility of printing inside each class. For example. Instead of:
if ($form instanceof Tuple) {
    return $this->printTuple($form);
}

Use $form->print() when required (usually inside their __toString() methods).

In my opinion, the ideal option is the second one, because each form type would be responsible for its own printing thing. BUT there is a problem with this approach, there are forms without types, also we are using native scalar types (this means not custom-defined types). This way I don’t find an easy way to do this second approach…

TL;DR: I would go for the first approach (Suggestion 1: Using extract class refactoring) as a first iteration to reduce the complexity of this existing class. And in another Issue/PR we could discuss the second option (adding custom-types for all forms).

Add a set data structure

Currently Phel has Tuples, Arrays and Tables. It would be nice to have a Set data structure based on PHP Arrays. For example:

(set :a :b :c)

Methods on a set:

  • union
  • intersection
  • difference
  • join

Cannot run simple hello-world

Hi,
Still working with the tutorial and getting a new problem. All the run commands fail

vendor/bin/phel run src/boot.phel
# or
vendor/bin/phel run hello-world\\boot
# or
vendor/bin/phel run "hello-world\boot"
vendor/bin/phel run "hello-world\boot"
RuntimeException: Cannot load namespace: hello-world\boot
in ...phel/hello-world/vendor/phel/phel/src/php/Commands/RunCommand.php:38 (gen: .../phel/hello-world/vendor/phel/phel/src/php/Commands/RunCommand.php:38)

#0 .../phel/hello-world/vendor/phel/phel/src/php/Main.php(105): Phel\Commands\RunCommand->run('/Users/ben/usrd...', 'hello-world\boo...')
#1 ...phel/hello-world/vendor/phel/phel/src/php/Main.php(81): Phel\Main->executeRunCommand()
#2 .../phel/hello-world/vendor/phel/phel/src/php/Main.php(129): Phel\Main->run()
#3 .../phel/hello-world/vendor/phel/phel/phel(5): require_once('/Users/ben/usrd...')

Here my composer.json

cat composer.json

{
    "name": "ben/hello-world",
    "type": "project",
    "authors": [
        {
            "name": "Ben Srctxt",
            "email": "[email protected]"
        }
    ],
    "require": {
        "phel/phel": "dev-master"
    },
    "extra": {
      "phel": {
         "loader": {
            "hello-world\\": "src/"
         }
      }
   },
    "minimum-stability": "dev"
}

Run phel script from the command line

I don't know if this is possible atm, but it would be nice to be able to execute phel scripts from the command line.

eg.

vendor/bin/phel ./boot.phel

Is this possible?

I could give this a try to implement if you want?

Could we adapt this code style?

Hy Style Guide

I have formatted code in this PR according to this style guide and it looks good and logical to me.

What do you think? Could we add this to the documentation and then in the future build a code formatter around this?

Bug: Reduce ignore the whole sequence if the first element is nil

Description

If the first element in the sequence passed to reduce is nil, it returns the initial value. Therefor, subsequent values are ignored.

  • (reduce + 42 [nil 1 2]) outputs 42
  • (+ 42 nil 1 2) outputs 45

Expected behavior

(reduce + 42 [nil 1 2]) should output 45 since it should behave the same as (+ 42 nil 1 2).

Where is Struct instanciated?

@jenshaase, I'm trying to find the usages of the Struct abstract class.

namespace Phel\Lang;
abstract class Struct extends Table {/* ... */}

And the only place where I found this class being used is in the Printer, in order to print the correct type:

// in Phel\Printer::print() function
final class Printer
{
    public function print($form, bool $readable): string 
    {
        // ...
        if ($form instanceof Struct) {
            return $this->printStruct($form, $readable);
        }
        // ...
    }
    private function printStruct(Struct $form, bool $readable): string
    {/* ... */}
}

Being Struct an abstract class, it can not be instantiated on its own (by new Struct).
I didn't see any other class extending this abstract class as well. So the question is the following:
Q: What's the purpose of this abstract class Struct then?

Cannot install Phel on macos

Hi,
I follow, the tutorial until the point where Phel should be installed by composer

Specs

Mac OS X, 10.15.6
composer --version
Composer version 1.10.13 2020-09-09 11:46:34

Error

composer require phel/phel:dev-master

./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Installation request for phel/phel dev-master -> satisfiable by phel/phel[dev-master].
    - phel/phel dev-master requires phel/phel-composer-plugin dev-master -> satisfiable by phel/phel-composer-plugin[dev-master] but these conflict with your requirements or minimum-stability.


Installation failed, reverting ./composer.json to its original content.

Change Readme for VSCode plugin

This Readme file is currently an automatically generated Readme file. It should contain basic information about the plugin and how to install it.

Bug: Require a module

Observations

It seems that sometimes, modules aren't correctly resolved.

First, if I require a module that doesn't exists, the program will just ignore it without any warning neither error. Second, as you can see in #65, I cannot require the module phel\test\http from phel\test\index. But if I remove the require statement to phel\http in phel\test\http, it works again. I've tried to use the keyword :as, but it didn't change anything. So I'm suspecting a conflict of name maybe.

Expected behavior

I want to get an error if I require a module that doesn't exist. And I should be able to require phel\test\http from phel\test\index.

RuntimeException: Cannot load namespace: hello-worldboot

Running vendor/bin/phel run hello-world\boot does not work, but running vendor/bin/phel run "hello-world\boot" does work.

This is the output that I get when running vendor/bin/phel run hello-world\boot:

RuntimeException: Cannot load namespace: hello-worldboot
in /home/mabasic/code/src/test/hello-world/vendor/phel/phel/src/php/Commands/Run.php:36 (gen: /home/mabasic/code/src/test/hello-world/vendor/phel/phel/src/php/Commands/Run.php:36)

PHP Notice:  Undefined index: args in /home/mabasic/code/src/test/hello-world/vendor/phel/phel/src/php/Exceptions/TextExceptionPrinter.php on line 106
PHP Warning:  Invalid argument supplied for foreach() in /home/mabasic/code/src/test/hello-world/vendor/phel/phel/src/php/Exceptions/TextExceptionPrinter.php on line 149
#0 /home/mabasic/code/src/test/hello-world/vendor/phel/phel/src/php/phel.php(52): Phel\Commands\Run->run()
#1 /home/mabasic/code/src/test/hello-world/vendor/phel/phel/phel(4): require_once('/home/mabasic/c...')

Update: When I use double backslash then it works: vendor/bin/phel run hello-world\\boot.

Local Phel variable names do not match PHP Variables names

In Phel the variable name can be like my-name. However, $my-name is not a valid PHP variable. Therefore, the variable name must be converted.

PHP variable names must be:

  • of form ^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$.
  • this is not allowed

In PHP code:

function isValidWord(string $word) {
  return preg_match("/^[a-zA-Z_\x80-\xff][\\\\a-zA-Z0-9_\x80-\xff]*$/", $word) && $word != 'this';
}

Docs: Wrong definition of a function

Observation

In the section of the doc about Functions and Recursion, and more precisely about Anonymous Function, you can find this example:

(fn [] 1 2 3) # A function that returns 3

But this code results in this error: "'fn requires one or two arguments".

Expected behavior

I'm not sure about what is the expected behavior, I'm just sure that the doc is wrong about this. So maybe someone more experimented on the project than me could help me to determine the expected behavior.

Use traits to factorize Phel classes implementations

As discussed here #62 (comment), we could factorize the implementation of Phel classes by using traits, since inheritance seems to not be the right way to do it. There is some function reused over the code like hash, count, current, next, key, valid, rewind or offsetHash that share the same implementation over PhelArray, Table, Tuple and soon Set.

Factorize this code would help to improve the maintainability of those classes and would make them way shorter, keeping only the code specific to the feature inside them.

Add syntax support for PhpStorm

Proposal: Test framework

Motivation

Phel should provide a default testing framework to test Phel code.

Concept

The code for the Phel's testing library is bundled in the namespace phel.test.

Assertions

The core of the library is the is macro to define assertions. For example

(is (= 4 (+ 2 2))
(is (true? (or true false))
(is (not (true? (and true false)))

The is macro supports following forms:

  • (predicate expected actual) like (= 4 (+ 2 2))
  • (predicate value) like (true? (or true false))
  • (not (predicate expected actual)) like (not (= 4 (+ 2 3)))
  • (not (predicate value)) like (not (true? false))
  • (thrown? exception-symbol body) like (thrown? Exception (/ 1 0))
  • (thrown-with-msg? exception-symbol exception-msg body) like (thrown? Exception "Division by zero" (/ 1 0))
  • (output-string expected-string body) like (output-string "test" (php/echo "test"))
  • anything else that evaluates to true or false

The is macro supports an additional second string argument to document the test case

(is (= 4 (+ 2 2)) "add operation")

An assertion can lead to the three states:

  • passed: When the form evaluates to true
  • failed: When the from evaluates to false
  • error: When the form could not be executed successfully (throws Exceptions)

Defining tests

To define a test the deftest macro can be used

(deftest my-add-test
  (is (= 4 (+ 2 2)))

Internally deftest is a defn with zero arguments. As metadata the defn has a :test attribute. So deftest is equivalent to

(defn my-add-test @{:test true} []
  (is (= 4 (+ 2 2)))

Running tests

The function run-tests can be used to executes tests in one or more namespaces

(run-tests 
  my-namespace\a 
  my-namespace\b)

Reporting

When executing the tests a reporter should print the current status of the test suite. While running it should print a . for a successful test a F for a failed test and a E for an error test. Afterwards it prints a list with details about all failures and errors. At the end a summary is printed (tests executed, tests passed, tests failed, tests with error).

Failure examples

(is (= 4 (+ 2 3)) "my test")
Failure in my test (path/to/file:123)
          Test: (+ 2 3)
  evaluated to: 5
    but is not: = to 4

(is (true? false) "my test")
Failure in my test (path/to/file:123)
                 Test: false
         evaluated to: false
  but doesn't satisfy: true?

(is (not (= 4 (+ 2 2))) "my test")
Failure in my test (path/to/file:123)
          Test: (+ 2 2)
  evaluated to: 4
        but is: = to 4 (it shouldn't be)

(is (not (true? true)) "my test")
Failure in my test (path/to/file:123)
              Test: true
      evaluated to: true
  but does satisfy: true? (it shouldn't)

Error examples

Error in path/to/file:123:
<Exception output (Message + StackTrace)>

Additional information

To make the API look nice, the namespace macro needs to be extended to support direct imports. Currently, it works the following way

(ns my-test-ns
  (:require phel\test :as test))

(test/deftest ...)

But it would be nice if we can omit the test/ prefix. Therefore, I would propose following addition

(ns my-test-ns
  (:require phel\test :as test :refer [deftest is]))

(deftest ...)

Docs: Documentation for the "phel" configuration section needs to be written

We need to document this: #52

In order to archive this, each library needs to provide a configuration with a namespace. I will suggest that we will put this configuration in the composer.json file under extra.phel.

{
    "extra": {
        "phel": {
            "loader": {
                "phel\\": "src/phel/"
            },
            "loader-dev": {
                "phel\\": "tests/phel/"
            }
        }
    }
}

I also added it as dependency to Phel and tested it with a sample project. Make sure you add "minimum-stability": "dev" to your tests-project's composer file. Otherwise you will get an error.

Link to composer plugin: https://github.com/jenshaase/phel-composer-plugin

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.