Giter Club home page Giter Club logo

cotowali's Introduction

We need financial support to continue development. Please become a sponsor.

Cotowali

A statically typed scripting language that transpile into POSIX sh

Website

License: MPL 2.0 Join Cotowali Discord

Concepts of Cotowali

  • Outputs shell script that is fully compliant with POSIX standards.
  • Simple syntax.
  • Simple static type system.
  • Syntax for shell specific feature like pipe and redirection.

Example

fn fib(n: int): int {
  if n < 2 {
    return n
  }
  return fib(n - 1) + fib(n - 2)
}

fn int |> twice() |> int {
   var n = 0
   read(&n)
   return n * 2
}

assert(fib(6) == 8)
assert((fib(6) |> twice()) == 16)

fn ...int |> sum() |> int {
  var v: int
  var res = 0
  while read(&v) {
    res += v
  }
  return res
}

fn ...int |> twice_each() |> ...int {
  var n: int
  while read(&n) {
    yield n * 2
  }
}

assert((seq(3) |> sum()) == 6)
assert((seq(3) |> twice_each() |> sum()) == 12)

// Call command by `@command` syntax with pipeline operator
assert(((1, 2) |> @awk('{print $1 + $2}')) == '3')

There are more examples

Installation

Use Konryu (cotowali installer written in cotowali)

curl -sSL https://konryu.cotowali.org | sh
# add to your shell config like .bashrc
export PATH="$HOME/.konryu/bin:$PATH"
eval "$(konryu init)"

Build from source

  1. Install required tools

  2. Build

    z build
  3. Install

    sudo z symlink
    # or
    sudo z install

How to use

# compile
lic examples/add.li

# execution
lic examples/add.li | sh
# or
lic run examples/add.li

Development

See docs/development.md

Docker

docker compose run dev

Author

zakuro <[email protected]>

Acknowledgements

Cotowali is supported by 2021 Exploratory IT Human Resources Project (The MITOU Program by IPA: Information-technology Promotion Agency, Japan.

cotowali's People

Contributors

andrelaszlo avatar mixmastamyk avatar pdietl avatar serjan-nasredin avatar zakuro9715 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

cotowali's Issues

POSIX Awk as a target

Perhaps it would be interesting to try to target AWK as an output. It's functions are not run in sub shells and it can do all math itself. I am thinking of this because of how horridly slow sh is at doing the raytracing example since it spends almost all its time calling other programs. AWK also has better array support.

Would you be interested if I attempted to contribute this?

"LINE 426: Assertion Failed" on "examples/raytracing.li"

tried examples/raytracing.li on zsh on MacOS Monterey 12.1, it produced an assersion error:
LINE 426: Assertion Failed

I thought I found the cause.
The variable obj includes 10 elements, and cotowali generates a code for the 10th argument with "$10". But it should be "${10}" as the document (https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html) says like:

When a positional parameter with more than one digit is specified, the application shall enclose the digits in braces

arithmetic expression error in array

code

var arr = []int{len: 100}
var n = 0
for i in range(0, 5) {
  arr[i] = i
  n += arr[i]
}

Expected behavior

Success

Actual behavior

sh: 665: arithmetic expression: expecting primary: " n += "

Output:

array_init "arr" 100 0
n=0
for _cotowali_tmp_36 in $(range 0 5)
do
  for_0_i="$(eval echo $_cotowali_tmp_36)"
  array_set arr "${for_0_i}" "${for_0_i}"
  n=$(( n += $(array_get arr for_0_i ) ))
done

Docker build crashes

(Running from repo root)

$ sudo docker-compose run dev 

Expected behavior
Building and running.

Actual behavior
See below.

[3/3] STEP 11/14: WORKDIR $COTOWALI_ROOT
--> Using cache e47c56a51eb4e0ad1d40675aca868b75747b9dd9d6a67554f6b3a43f6e1ecc6d
--> e47c56a51eb
[3/3] STEP 12/14: COPY . .
--> 3b13ea09ce7
[3/3] STEP 13/14: RUN z build && z symlink
cotowali/ast/expr.v:72:12: error: `expr` is immutable, declare it with `mut` to make it mutable
   70 | 
   71 | fn (mut r Resolver) expr(expr Expr, opt ResolveExprOpt) {
   72 |     match mut expr {
      |               ~~~~
   73 |         ArrayLiteral { r.array_literal(mut expr, opt) }
   74 |         AsExpr { r.as_expr(expr, opt) }
cotowali/ast/expr.v:234:19: error: `e` is immutable, declare it with `mut` to make it mutable
  232 | 
  233 | pub fn (e Expr) typ() Type {
  234 |     return match mut e {
      |                      ^
  235 |         ArrayLiteral { e.scope.lookup_or_register_array_type(elem: e.elem_typ).typ }
  236 |         AsExpr { e.typ }
cotowali/ast/stmt.v:51:12: error: `stmt` is immutable, declare it with `mut` to make it mutable
   49 | 
   50 | fn (mut r Resolver) stmt(stmt Stmt) {
   51 |     match mut stmt {
      |               ~~~~
   52 |         AssertStmt { r.assert_stmt(stmt) }
   53 |         AssignStmt { r.assign_stmt(mut stmt) }
cotowali/ast/stmt.v:168:13: error: `left` is immutable, declare it with `mut` to make it mutable
  166 |                 }
  167 |                 for i, left in stmt.left.exprs {
  168 |                     if mut left is Var {
      |                            ~~~~
  169 |                         name, pos := left.ident.text, left.ident.pos
  170 |                         typ := if i < expr_types.len {
cotowali/checker/stmt.v:71:12: error: `stmt` is immutable, declare it with `mut` to make it mutable
   69 |     }
   70 | 
   71 |     match mut stmt {
      |               ~~~~
   72 |         ast.AssignStmt { c.assign_stmt(mut stmt) }
   73 |         ast.AssertStmt { c.assert_stmt(stmt) }
cotowali/emit/sh/array.v:13:33: error: cannot use `fn (mut sh.Emitter, ast.ArrayLiteral, string>)` as `fn (mut sh.Emitter, T)` in argument 2 to `cotowali.emit.sh.Emitter.insert_at`
   11 | fn (mut e Emitter) array_literal(expr ast.ArrayLiteral, opt ExprOpt) {
   12 |     ident := e.ident_for(expr)
   13 |     e.insert_at(e.stmt_head_pos(), fn (mut e Emitter, v ExprWithValue<ast.ArrayLiteral, string>) {
      |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   14 |         ident := v.value
   15 |         e.assign(ident, ast.Expr(v.expr), ast.Expr(v.expr).type_symbol())
cotowali/emit/sh/array.v:58:31: error: cannot use `fn (mut sh.Emitter, ast.InfixExpr)` as `fn (mut sh.Emitter, T)` in argument 1 to `cotowali.emit.sh.Emitter.sh_test_command_for_expr`
   56 |     match expr.op.kind {
   57 |         .eq, .ne {
   58 |             e.sh_test_command_for_expr(fn (mut e Emitter, expr ast.InfixExpr) {
      |                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   59 |                 e.array_to_str(expr.left)
   60 |                 e.write(if expr.op.kind == .eq { ' = ' } else { ' != ' })
cotowali/emit/sh/array.v:66:35: error: cannot use `fn (mut sh.Emitter, ast.InfixExpr, string>)` as `fn (mut sh.Emitter, T)` in argument 2 to `cotowali.emit.sh.Emitter.insert_at`
   64 |         .plus {
   65 |             ident := e.new_tmp_ident()
   66 |             e.insert_at(e.stmt_head_pos(), fn (mut e Emitter, v ExprWithValue<ast.InfixExpr, string>) {
      |                                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   67 |                 expr, ident := v.expr, v.value
   68 |                 e.write('array_copy $ident ')
cotowali/emit/sh/assign.v:78:29: error: cannot use `fn (mut sh.Emitter, sh.ExprOrString)` as `fn (mut sh.Emitter, T)` in argument 2 to `cotowali.emit.sh.Emitter.sh_define_function`
   76 | 
   77 | fn (mut e Emitter) fn_assign(name string, value ExprOrString) {
   78 |     e.sh_define_function(name, fn (mut e Emitter, value ExprOrString) {
      |                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   79 |         target := if value is string { value } else { e.ident_for(value as ast.Expr) }
   80 |         e.writeln(target + r' "$@"') // passthrough arguments
cotowali/emit/sh/expr.v:109:33: error: cannot use `fn (mut sh.Emitter, util.Tuple2<string, string>)` as `fn (mut sh.Emitter, T)` in argument 2 to `cotowali.emit.sh.Emitter.insert_at`
  107 | 
  108 |     tmp_var := e.new_tmp_ident()
  109 |     e.insert_at(e.stmt_head_pos(), fn (mut e Emitter, v Tuple2<string, string>) {
      |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  110 |         tmp_var, text := v.v1, v.v2
  111 |         e.write('$tmp_var="\$(')
cotowali/emit/sh/expr.v:194:28: error: cannot use `fn (mut sh.Emitter, ast.IndexExpr>)` as `fn (mut sh.Emitter, T)` in argument 1 to `cotowali.emit.sh.Emitter.sh_command_substitution`
  192 |     e.write_echo_if_command(opt)
  193 | 
  194 |     e.sh_command_substitution(fn (mut e Emitter, v ExprWithOpt<ast.IndexExpr>) {
      |                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  195 |         left_ts := v.expr.left.type_symbol().resolved()
  196 |
cotowali/emit/sh/expr.v:278:30: error: cannot use `fn (mut sh.Emitter, ast.InfixExpr)` as `fn (mut sh.Emitter, T)` in argument 1 to `cotowali.emit.sh.Emitter.sh_test_command_for_expr`
  276 | 
  277 |     if expr.op.kind in [.eq, .ne] {
  278 |         e.sh_test_command_for_expr(fn (mut e Emitter, expr ast.InfixExpr) {
      |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  279 |             op := if expr.op.kind == .eq { ' = ' } else { ' != ' }
  280 |             e.sh_test_cond_infix(expr.left, op, expr.right)
cotowali/emit/sh/expr.v:327:30: error: cannot use `fn (mut sh.Emitter, ast.InfixExpr)` as `fn (mut sh.Emitter, T)` in argument 1 to `cotowali.emit.sh.Emitter.sh_test_command_for_expr`
  325 | 
  326 |     if expr.op.kind.@is(.comparsion_op) {
  327 |         e.sh_test_command_for_expr(fn (mut e Emitter, expr ast.InfixExpr) {
      |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  328 |             e.sh_awk_infix_expr(expr)
  329 |             e.write(' -eq 1')
cotowali/emit/sh/expr.v:346:30: error: cannot use `fn (mut sh.Emitter, ast.InfixExpr)` as `fn (mut sh.Emitter, T)` in argument 1 to `cotowali.emit.sh.Emitter.sh_test_command_for_expr`
  344 | 
  345 |     if expr.op.kind.@is(.comparsion_op) {
  346 |         e.sh_test_command_for_expr(fn (mut e Emitter, expr ast.InfixExpr) {
      |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  347 |             op := match expr.op.kind {
  348 |                 .eq { '-eq' }
cotowali/emit/sh/expr.v:391:31: error: cannot use `fn (mut sh.Emitter, ast.InfixExpr)` as `fn (mut sh.Emitter, T)` in argument 1 to `cotowali.emit.sh.Emitter.sh_test_command_for_expr`
  389 |     match expr.op.kind {
  390 |         .eq, .ne {
  391 |             e.sh_test_command_for_expr(fn (mut e Emitter, expr ast.InfixExpr) {
      |                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  392 |                 op := if expr.op.kind == .eq { ' = ' } else { ' != ' }
  393 |                 e.sh_test_cond_infix(expr.left, op, expr.right)
cotowali/emit/sh/expr.v:417:31: error: cannot use `fn (mut sh.Emitter, ast.InfixExpr)` as `fn (mut sh.Emitter, T)` in argument 1 to `cotowali.emit.sh.Emitter.sh_test_command_for_expr`
  415 |     match expr.op.kind {
  416 |         .eq, .ne {
  417 |             e.sh_test_command_for_expr(fn (mut e Emitter, expr ast.InfixExpr) {
      |                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  418 |                 op := if expr.op.kind == .eq { ' = ' } else { ' != ' }
  419 |                 e.sh_test_cond_infix(expr.left, op, expr.right)
cotowali/emit/sh/expr.v:534:29: error: cannot use `fn (mut sh.Emitter, ast.Pipeline)` as `fn (mut sh.Emitter, T)` in argument 1 to `cotowali.emit.sh.Emitter.sh_command_substitution`
  532 |         f(mut e, expr)
  533 |     } else {
  534 |         e.sh_command_substitution(f, expr, opt)
      |                                   ^
  535 |     }
  536 | }
cotowali/emit/sh/fn.v:116:33: error: cannot use `fn (mut sh.Emitter, ast.FnDecl)` as `fn (mut sh.Emitter, T)` in argument 2 to `cotowali.emit.sh.Emitter.sh_define_function`
  114 |     }
  115 | 
  116 |     e.sh_define_function(fn_ident, fn (mut e Emitter, node FnDecl) {
      |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  117 |         for i, param in node.params {
  118 |             value := if i == node.params.len - 1 && node.function_info().variadic {
cotowali/emit/sh/map.v:12:33: error: cannot use `fn (mut sh.Emitter, ast.MapLiteral, string>)` as `fn (mut sh.Emitter, T)` in argument 2 to `cotowali.emit.sh.Emitter.insert_at`
   10 | fn (mut e Emitter) map_literal(expr MapLiteral, opt ExprOpt) {
   11 |     ident := e.ident_for(expr)
   12 |     e.insert_at(e.stmt_head_pos(), fn (mut e Emitter, v ExprWithValue<MapLiteral, string>) {
      |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   13 |         ident := v.value
   14 |         for entry in v.expr.entries {
cotowali/emit/sh/string_literal.v:123:33: error: cannot use `fn (mut sh.Emitter, ast.StringLiteral, string>)` as `fn (mut sh.Emitter, T)` in argument 2 to `cotowali.emit.sh.Emitter.insert_at`
  121 | 
  122 |     tmp_var := e.new_tmp_ident()
  123 |     e.insert_at(e.stmt_head_pos(), fn (mut e Emitter, arg ExprWithValue<StringLiteral, string>) {
      |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  124 |         expr := arg.expr
  125 |         tmp_var := arg.value
exit status 1
exit status 1

Shell builder

今は AST から文字列を直接吐いているが、間違ったシェルを吐きやすいので、間に shell builder をかませる

Tester is too complex

tests/tester.v is too complex.

  1. rewrite tester more simply
  2. Add builtin test runner to cotowali itself (like v test)

named tuple

require #15

var v1 = (a: 0, b: 1)
var v2: (a: int, b: int) = (0, 1)
assert v1.a == 0
assert v1 == v2

Universal Shell Script

# get-process が存在すれば pwsh.         | ここからは sh では実行されない部分だが
# if 文は文法が違うので、&& で分岐       | 解釈はされるので注意が必要 invoke-command {} はうまく動かない
get-process > ${null:-/dev/null} 2>&1 && (invoke-expression @'
  # pwsh 用コード実行部分

  # base64エンコードされたソースコード ( echo "hello pwsh" )
  # 実際には cotowali の pwsh バックエンドが生成した ps1 スクリプトをエンコードして注入する
  $code_b64="ZWNobyAiaGVsbG8gcHdzaCIK"
  # デコードして実行
  $code_bytes=[System.Convert]::FromBase64String($code_b64)
  $code=[System.Text.Encoding]::Default.GetString($code_bytes)
  invoke-expression $code
  # 後段の sh 用の部分が実行されないようにここで終了
  exit 0
'@)

# sh 用コード
# posix には base64 が存在しないので、cotowali の出力をそのまま張り付ける
# powershell はここに到達せずに exit するため、これ以降を解釈することはなく、sh のコードそのままでOK。
echo 'hello sh'

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.