oils-for-unix / oils

Oils is our upgrade path from bash to a better language and runtime. It's also for Python and JavaScript users who avoid shell!
http://www.oilshell.org/
Other
2.85k stars 158 forks source link

Configuration Dialect #951

Open andychu opened 3 years ago

andychu commented 3 years ago

Summary: YAML alternative / COWS: configuration in Oil without Side effects

This is part of #631: Oil Blocks

Rough sketch of what we need:

Example of evalblock

proc p(name, &myblock) {
  var d = evalblock(myblock)
  setvar d->name = name

  json write :d
  setvar _cows = d     # does this make sense?
}

proc localized(@names) {
  for name in @names {
    p $name {
       age = 30
       ratio = 3.0
    }
  }
}

# top level configuration

p myname {
  age = 30
  ratio = 0.1
}
p other {
  age = 42
  ratio = 0.2
}

# macro-ized configuration

localized 

How does scope work?

andychu commented 3 years ago

Config Dialect Use cases -- MOVED TO https://github.com/oilshell/oil/wiki/Config-Dialect

andychu commented 3 years ago

Naming idea: evalcows()

Just to recap:

idea:

# eval-config.oil

proc server(name, &b) {
  push-context route {  # define route
    var d = evalblock(b)
  }
}

dialect --name myproject -- server otherproc {
  source $1  # the filename ?  Does it give good errors?
}

# config.cows

use dialect myproject  # ensure it's running under this dialect

server www.example.com {
  root = '/'
  port = 80
}

Use cases

andychu commented 3 years ago

Idea for evalblock() semantics

Does value_t need a span_id then? I think I would find that useful in some cases, like the dynamic const check? If it's already defined?

andychu commented 2 years ago

Instead of evalblock(), we have #1025

andychu commented 2 years ago

From Zulip, an idea to make blocks syntactic sugar for dicts, and then gracefully allow more validation with procs:

https://oilshell.zulipchat.com/#narrow/stream/121540-oil-discuss/topic/Using.20oil.20to.20represent.20complex.20data


However I'd still like to define the valid first words, so you could have

eval ('myfile.oil', procs=%(package user))

And then 'package' and 'user' could be ways to define dicts, like

package cppunit

user bob

would turn into

{ "type": "package",
  "name": "cppunit"
}

{ "type": "user",
  "name": "bob"
}
andychu commented 2 years ago

New minimal idea (in NOTES.txt)

Is that it?

Issue for later:

andychu commented 2 years ago

Evaluation rules:

andychu commented 2 years ago

More isolation issues:

Doh so we need to restrict the builtins dictionary ...

Or we can rely on containers to do this?

It is an argument for always forking a process ... But then how do we get procs from one place to the other?

I think we don't want push-procs. I think we want to have an inherent source before the config file evaluation

bin/oven --source mydialect.oil -- myconfig.oil

It's a bit more complicated that way but probably more general and secure ... we don't want a blacklisting approach; whitelisting is better.

Although push-procs is whitelisting of procs -- it's not whitelisting of builtins like source though

andychu commented 2 years ago

Although Executor holds both self.builtins and self.procs, so we could have

push (builtins = %(echo printf json), procs=%(foo bar)) {
  shvar PATH='' {
    const d = eval_to_dict(myblock)
  }
}
andychu commented 2 years ago

First cut of hay works! After a huge commit

https://github.com/oilshell/oil/commit/47c972391969bf59d9d421abad81bfdae74019e2

More:

Related issues

andychu commented 2 years ago
andychu commented 2 years ago

More:

After some usage and feedback:

bar-g commented 2 years ago

Had a look into the new doc/hay skeleton, and as you have mentioned DSLs there, and I still had your comment about the common push / pop issue in mind (https://github.com/oilshell/oil/issues/1059#issuecomment-1151734946), I felt maybe there is some common solution there to find.

Ruby/crystal seems to have two types of blocks for DSLs:

The difference between using do ... end and { ... } is that do ... end binds to the left-most call, while { ... } binds to the right-most call. The reason for this is to allow creating Domain Specific Languages (DSLs) using do ... end to have them be read as plain English: [...] You wouldn't want the above to be: [...] (https://crystal-lang.org/reference/1.4/syntax_and_semantics/blocks_and_procs.html)

Maybe some kind of general apply/with/to/do or and? -block might help for DSLs, too?

andychu commented 2 years ago

Yeah there is definitely possibility of things like that ... though I want people to use it first, and actually hit the problem, before we add complexity!

I'm pretty happy with the simple first pass, and hopefully we'll get a bunch of feedback on it

This feature is a bit more obscure than others, because I think platforms / maintainers will generally use it, not end users. i.e. it's for people building something like sourcehut, Github Actions, systemd, podman/Docker, etc.

bar-g commented 2 years ago

Sure, a universal/left/right block application idea/rules will likely need some time to ripe anyway.

I had thought hay could be nice even just for exposing simplest config files to users, thanks to the sandboxing, safe to "source", no need to write parsing code? But I didn't grasp the whole usage/practical picture just yet.

bar-g commented 1 year ago

Something in your latest "Sketches of YSH Features" blog and your intention for ysh to be well-extensible by users, made me remember my comments here. You wrote:

[proc] Arguments are bound left to right, with splats like ... picking up the rest, except for the block argument. It's always the last argument, so it's neither positional or named.

This sounds as if it wouldn't allow to pass multiple blocks.

I'm not sure if that would still allow for exposing/allowing for more generic block usage, i.e. having left and right binding blocks (for better readable DSLs https://github.com/oilshell/oil/issues/951#issuecomment-1152953400), as well as making use of hay-based config in ysh itself (e.g. no fuzz readable redirection syntax https://github.com/oilshell/oil/issues/1059#issuecomment-1137481928), and generic apply {...} to {...} block application or "operators".

With code+data in the blocks (i.e. hay) could that maybe be a useful basis to allow for implementing, e.g. said matrix algebra operators etc., as extensions as ysh libraries instead of compiled code?

andychu commented 1 year ago

I think you can only pass on block in the {} style, as the last arg

But there's nothing stopping you from passing multiple blocks the expression style, like

myproc (block1, block2)

It's mainly a syntactic limitation

But yeah when we implement that, we should have some test cases for it