lang: ast: The res and edge names should not use exclusives

This removes the exclusive from the res names and edge names. We now
require that the names should be lists of strings, however they can
still be single strings if that can be determined statically.
Programmers should explicitly wrap their variables in a string by
interpolation to force this, or in square brackets to force a list. The
former is generally preferable because it generates a small function
graph since it doesn't need to build a list.
This commit is contained in:
James Shubin
2024-04-18 00:07:53 -04:00
parent dc45c90ccd
commit 51cf1e2921
175 changed files with 500 additions and 378 deletions

View File

@@ -283,6 +283,14 @@ one of many ways you can perform iterative tasks that you might have
traditionally used a `for` loop for instead. This is preferred, because flow
control is error-prone and can make for less readable code.
The single `str` variation, may only be used when it is possible for the
compiler to determine statically that the value is of that type. Otherwise, it
will assume it to be a list of strings. Programmers should explicitly wrap their
variables in a string by interpolation to force this static `str` determination,
or in square brackets to force a list. The former is generally preferable
because it generates a smaller function graph since it doesn't need to build a
list.
##### Internal edges
Resources may also declare edges internally. The edges may point to or from
@@ -337,6 +345,28 @@ to express a relationship between three resources. The first character in the
resource kind must be capitalized so that the parser can't ascertain
unambiguously that we are referring to a dependency relationship.
##### Edge naming
Each edge must have a unique name of type `str` that is used to uniquely
identify that edge, and can be used in the functioning of the edge at its
discretion.
Alternatively, the name value may be a list of strings `[]str` to build a list
of edges, each with a name from that list.
Using this construct is a veiled form of looping (iteration). This technique is
one of many ways you can perform iterative tasks that you might have
traditionally used a `for` loop for instead. This is preferred, because flow
control is error-prone and can make for less readable code.
The single `str` variation, may only be used when it is possible for the
compiler to determine statically that the value is of that type. Otherwise, it
will assume it to be a list of strings. Programmers should explicitly wrap their
variables in a string by interpolation to force this static `str` determination,
or in square brackets to force a list. The former is generally preferable
because it generates a smaller function graph since it doesn't need to build a
list.
#### Class
A class is a grouping structure that bind's a list of statements to a name in

View File

@@ -326,6 +326,10 @@ func (obj *StmtBind) Output(map[interfaces.Func]types.Value) (*interfaces.Output
// value can be a single string or a list of strings. The former will produce a
// single resource, the latter produces a list of resources. Using this list
// mechanism is a safe alternative to traditional flow control like `for` loops.
// The `Name` value can only be a single string when it can be detected
// statically. Otherwise, it is assumed that a list of strings should be
// expected. More mechanisms to determine if the value is static may be added
// over time.
// TODO: Consider expanding Name to have this return a list of Res's in the
// Output function if it is a map[name]struct{}, or even a map[[]name]struct{}.
type StmtRes struct {
@@ -576,39 +580,35 @@ func (obj *StmtRes) Unify() ([]interfaces.Invariant, error) {
}
invariants = append(invariants, invars...)
invarStr := &interfaces.EqualsInvariant{
Expr: obj.Name,
Type: types.TypeStr,
}
// Optimization: If we know it's an str, no need for exclusives!
// TODO: Check other cases, like if it's a function call, and we know it
// can only return a single string. (Eg: fmt.printf for example.)
isString := false
if _, ok := obj.Name.(*ExprStr); ok {
invariants = append(invariants, invarStr)
// It's a string! (A plain string was specified.)
isString = true
}
if typ, err := obj.Name.Type(); err == nil {
// It has type of string! (Might be an interpolation specified.)
if typ.Cmp(types.TypeStr) == nil {
isString = true
}
}
if isString {
invar := &interfaces.EqualsInvariant{
Expr: obj.Name,
Type: types.TypeStr,
}
invariants = append(invariants, invar)
return invariants, nil
}
invarListStr := &interfaces.EqualsInvariant{
// Down here, we only allow []str, no need for exclusives!
invar := &interfaces.EqualsInvariant{
Expr: obj.Name,
Type: types.TypeListStr,
}
// Optimization: If we know it's a []str, no need for exclusives!
if expr, ok := obj.Name.(*ExprList); ok {
typ, err := expr.Type()
if err == nil && typ.Cmp(types.TypeListStr) == nil {
invariants = append(invariants, invarListStr)
return invariants, nil
}
}
// name must be a string or a list
ors := []interfaces.Invariant{}
ors = append(ors, invarStr)
ors = append(ors, invarListStr)
invar := &interfaces.ExclusiveInvariant{
Invariants: ors, // one and only one of these should be true
}
invariants = append(invariants, invar)
return invariants, nil
@@ -2379,7 +2379,13 @@ func (obj *StmtEdge) Output(table map[interfaces.Func]types.Value) (*interfaces.
}
// StmtEdgeHalf represents half of an edge in the parsed edge representation.
// This does not satisfy the Stmt interface.
// This does not satisfy the Stmt interface. The `Name` value can be a single
// string or a list of strings. The former will produce a single edge half, the
// latter produces a list of resources. Using this list mechanism is a safe
// alternative to traditional flow control like `for` loops. The `Name` value
// can only be a single string when it can be detected statically. Otherwise, it
// is assumed that a list of strings should be expected. More mechanisms to
// determine if the value is static may be added over time.
type StmtEdgeHalf struct {
Kind string // kind of resource, eg: pkg, file, svc, etc...
Name interfaces.Expr // unique name for the res of this kind
@@ -2488,39 +2494,35 @@ func (obj *StmtEdgeHalf) Unify() ([]interfaces.Invariant, error) {
}
invariants = append(invariants, invars...)
invarStr := &interfaces.EqualsInvariant{
Expr: obj.Name,
Type: types.TypeStr,
}
// Optimization: If we know it's an str, no need for exclusives!
// TODO: Check other cases, like if it's a function call, and we know it
// can only return a single string. (Eg: fmt.printf for example.)
isString := false
if _, ok := obj.Name.(*ExprStr); ok {
invariants = append(invariants, invarStr)
// It's a string! (A plain string was specified.)
isString = true
}
if typ, err := obj.Name.Type(); err == nil {
// It has type of string! (Might be an interpolation specified.)
if typ.Cmp(types.TypeStr) == nil {
isString = true
}
}
if isString {
invar := &interfaces.EqualsInvariant{
Expr: obj.Name,
Type: types.TypeStr,
}
invariants = append(invariants, invar)
return invariants, nil
}
invarListStr := &interfaces.EqualsInvariant{
// Down here, we only allow []str, no need for exclusives!
invar := &interfaces.EqualsInvariant{
Expr: obj.Name,
Type: types.TypeListStr,
}
// Optimization: If we know it's a []str, no need for exclusives!
if expr, ok := obj.Name.(*ExprList); ok {
typ, err := expr.Type()
if err == nil && typ.Cmp(types.TypeListStr) == nil {
invariants = append(invariants, invarListStr)
return invariants, nil
}
}
// name must be a string or a list
ors := []interfaces.Invariant{}
ors = append(ors, invarStr)
ors = append(ors, invarListStr)
invar := &interfaces.ExclusiveInvariant{
Invariants: ors, // one and only one of these should be true
}
invariants = append(invariants, invar)
return invariants, nil

View File

@@ -65,7 +65,7 @@ class base($config) {
panic($prefix == "") # panic if prefix is empty
panic(not strings.has_suffix($prefix, "/"))
file $prefix { # dir
file "${prefix}" { # dir
state => $const.res.file.state.exists,
}
$tftp_prefix = "${prefix}${tftp_suffix}"
@@ -85,7 +85,7 @@ class base($config) {
#
# network
#
net $interface {
net "${interface}" {
state => $const.res.net.state.up,
addrs => [$router,], # has cidr suffix
#gateway => "192.168.42.1", # TODO: get upstream public gateway with new function
@@ -130,10 +130,10 @@ class base($config) {
}
}
file $tftp_prefix { # dir
file "${tftp_prefix}" { # dir
state => $const.res.file.state.exists,
}
file $uefi_prefix { # dir
file "${uefi_prefix}" { # dir
state => $const.res.file.state.exists,
}
@@ -155,7 +155,7 @@ class base($config) {
# XXX: should this also be part of repo too?
class tftp_root_file($f) {
#tftp:file $f { # without root slash
#tftp:file "${f}" { # without root slash
tftp:file "/${f}" { # with root slash
path => $syslinux_root + $f, # TODO: add autoedges
@@ -185,7 +185,7 @@ class base($config) {
#
# http
#
file $http_prefix { # dir
file "${http_prefix}" { # dir
state => $const.res.file.state.exists,
}
@@ -195,7 +195,7 @@ class base($config) {
}
$kickstart_http_prefix = "${http_prefix}${kickstart_suffix}"
file $kickstart_http_prefix {
file "${kickstart_http_prefix}" {
state => $const.res.file.state.exists,
#source => "", # this default means empty directory
recurse => true,
@@ -237,21 +237,21 @@ class base:repo($config) {
$distroarch_release_http_prefix = "${distroarch_http_prefix}release/"
$distroarch_updates_http_prefix = "${distroarch_http_prefix}updates/"
file $distroarch_tftp_prefix { # dir
file "${distroarch_tftp_prefix}" { # dir
state => $const.res.file.state.exists,
#Meta:quiet => true, # TODO
}
file $distroarch_uefi_prefix { # dir
file "${distroarch_uefi_prefix}" { # dir
state => $const.res.file.state.exists,
}
file $distroarch_http_prefix { # root http dir
file "${distroarch_http_prefix}" { # root http dir
state => $const.res.file.state.exists,
}
file $distroarch_release_http_prefix {
file "${distroarch_release_http_prefix}" {
state => $const.res.file.state.exists,
}
file $distroarch_updates_http_prefix {
file "${distroarch_updates_http_prefix}" {
state => $const.res.file.state.exists,
}
@@ -261,7 +261,7 @@ class base:repo($config) {
$uefi_download_dir = "${distroarch_uefi_prefix}download/"
$uefi_extract_dir = "${distroarch_uefi_prefix}extract/"
file $uefi_extract_dir { # mkdir
file "${uefi_extract_dir}" { # mkdir
state => $const.res.file.state.exists,
Depend => Exec["uefi-download-${uid}"],
@@ -609,7 +609,7 @@ class base:host($name, $config) {
}
$kickstart_file = "${kickstart_http_prefix}${hkey}.ks"
file $kickstart_file {
file "${kickstart_file}" {
state => $const.res.file.state.exists,
content => template(deploy.readfile("/files/kickstart.ks.tmpl"), $http_kickstart_template),
}
@@ -631,11 +631,11 @@ class base:host($name, $config) {
#kv "${name}" {
# key => $provision_key,
#}
#value $provision_key {
#value "${provision_key}" {
# #any => true, # bool
#}
#Http:Flag["${name}"].value -> Kv["${name}"].value
#Http:Flag["${name}"].value -> Value[$provision_key].any
#Http:Flag["${name}"].value -> Value["${provision_key}"].any
##$st_provisioned = value.get_bool($provision_key)
#$st_provisioned = value.get_str($provision_key)
#$provisioned = $st_provisioned->ready and $st_provisioned->value == "true" # export this value to parent scope

View File

@@ -18,8 +18,8 @@ $fn2 = funcgen(false)
$out1 = $fn1()
$out2 = $fn2()
test $out1 {}
test $out2 {}
test "${out1}" {}
test "${out2}" {}
-- OUTPUT --
Edge: FuncValue -> call # fn
Edge: FuncValue -> call # fn

View File

@@ -3,11 +3,12 @@ import "fmt"
include c1([13, 42, 0, -37,])
class c1($b) {
test fmt.printf("len is: %d", len($b)) {} # len is 4
test [fmt.printf("len is: %d", len($b)),] {} # len is 4
}
-- OUTPUT --
Edge: FuncValue -> call # fn
Edge: FuncValue -> call # fn
Edge: call -> composite # 0
Edge: const -> composite # 0
Edge: const -> composite # 1
Edge: const -> composite # 2
@@ -17,6 +18,7 @@ Vertex: FuncValue
Vertex: call
Vertex: call
Vertex: composite
Vertex: composite
Vertex: const
Vertex: const
Vertex: const

View File

@@ -6,7 +6,7 @@ $some_value1 = 42 # or something more complex like the output of a slow function
class foo($num) {
# we should have a different `$inside` value for each use of this class
$inside = $some_value1 + $some_value2 + 4
test fmt.printf("test-%d-%d", $num, $inside) {} # some resource
test [fmt.printf("test-%d-%d", $num, $inside),] {} # some resource
}
$some_value2 = 13 # check that non-ordering works too!
@@ -24,6 +24,9 @@ Edge: FuncValue -> call # fn
Edge: FuncValue -> call # fn
Edge: FuncValue -> call # fn
Edge: FuncValue -> call # fn
Edge: call -> composite # 0
Edge: call -> composite # 0
Edge: call -> composite # 0
Vertex: FuncValue
Vertex: FuncValue
Vertex: FuncValue
@@ -42,6 +45,9 @@ Vertex: call
Vertex: call
Vertex: call
Vertex: call
Vertex: composite
Vertex: composite
Vertex: composite
Vertex: const
Vertex: const
Vertex: const

View File

@@ -2,7 +2,7 @@
include c1("t1")
include c1("t2")
class c1($a) {
test $a {
test "${a}" {
stringptr => $foo,
}
}

View File

@@ -7,8 +7,8 @@ $prefixer = func($x) {
$out1 = $prefixer("a")
$out2 = $prefixer("b")
test $out1 {} # helloa
test $out2 {} # hellob
test "${out1}" {} # helloa
test "${out2}" {} # hellob
-- OUTPUT --
Edge: FuncValue -> call # fn
Edge: FuncValue -> call # fn

View File

@@ -1,8 +1,7 @@
-- main.mcl --
# this is an empty list of test resources, iow test resources
# this must pass type unification
# this can only currently pass if we allow recursive unification solving
# if we do, then the function graph is: `Vertex: list()` otherwise it's an error
# This is an empty list of test resources, iow test resources. This must pass
# type unification. This previously only passed if we allowed recursive
# unification solving, but now we can support it without that.
test [] {}
-- OUTPUT --
# err: errUnify: only recursive solutions left
Vertex: composite

View File

@@ -9,8 +9,8 @@ $prefixer = func($x) {
$out1 = $prefixer("world")
$out2 = $prefixer($out1)
test $out1 {}
test $out2 {}
test "${out1}" {}
test "${out2}" {}
-- OUTPUT --
Edge: FuncValue -> call # fn
Edge: FuncValue -> call # fn

View File

@@ -5,7 +5,7 @@ import "fmt"
include c1("t1", "hello") # len is 5
include c1("t2", [13, 42, 0, -37,]) # len is 4
class c1($a, $b) {
test $a {
test "${a}" {
anotherstr => fmt.printf("len is: %d", len($b)),
}
}

View File

@@ -7,7 +7,7 @@ include c1("t2", [13, 42, 0, -37,]) # len is 4
# specifying a fixed type for $b is a compile error, because it's sometimes str!
class c1($a, $b []str) {
test $a {
test "${a}" {
anotherstr => fmt.printf("len is: %d", len($b)),
}
}

View File

@@ -9,7 +9,7 @@ func funcgen() {
$fn = funcgen()
$out = $fn()
test $out {}
test "${out}" {}
-- OUTPUT --
Edge: FuncValue -> call # fn
Edge: call -> call # fn

View File

@@ -8,7 +8,7 @@ $funcgen = func() {
$fn = $funcgen()
$out = $fn()
test $out {}
test "${out}" {}
-- OUTPUT --
Edge: FuncValue -> call # fn
Edge: call -> call # fn

View File

@@ -4,7 +4,7 @@ $x = "hello"
if true {
$x = "world" # shadowed
}
test $x {}
test "${x}" {}
-- OUTPUT --
Vertex: const
Vertex: const

View File

@@ -3,7 +3,7 @@
$x = "hello"
if true {
$x = "world" # shadowed
test $x {}
test "${x}" {}
}
-- OUTPUT --
Vertex: const

View File

@@ -5,7 +5,7 @@ func answer() {
$out1 = answer()
test $out1 {}
test "${out1}" {}
-- OUTPUT --
Edge: FuncValue -> call # fn
Vertex: FuncValue

View File

@@ -6,15 +6,17 @@ func answer() {
$out1 = answer()
$out2 = answer()
test $out1 + $out2 {}
test [$out1 + $out2,] {}
-- OUTPUT --
Edge: FuncValue -> call # fn
Edge: FuncValue -> call # fn
Edge: FuncValue -> call # fn
Edge: call -> composite # 0
Vertex: FuncValue
Vertex: FuncValue
Vertex: FuncValue
Vertex: call
Vertex: call
Vertex: call
Vertex: composite
Vertex: const

View File

@@ -8,7 +8,7 @@ $answer = func() {
$out = $answer()
test $out {}
test "${out}" {}
-- OUTPUT --
Edge: FuncValue -> call # fn
Vertex: FuncValue

View File

@@ -9,14 +9,16 @@ $answer = func() {
$out1 = $answer()
$out2 = $answer()
test $out1 + $out2 {}
test [$out1 + $out2,] {}
-- OUTPUT --
Edge: FuncValue -> call # fn
Edge: FuncValue -> call # fn
Edge: FuncValue -> call # fn
Edge: call -> composite # 0
Vertex: FuncValue
Vertex: FuncValue
Vertex: call
Vertex: call
Vertex: call
Vertex: composite
Vertex: const

View File

@@ -11,6 +11,6 @@ $fn = if $some_bool {
}
$out = $fn("wide")
test $out {}
test "${out}" {}
-- OUTPUT --
Vertex: test[worldwide]

View File

@@ -11,6 +11,6 @@ $fn = if $some_bool {
}
$out = $fn(false)
test $out {}
test "${out}" {}
-- OUTPUT --
Vertex: test[world]

View File

@@ -11,6 +11,6 @@ $fn1 = funcgen()
$fn2 = $fn1()
$out = $fn2()
test $out {}
test "${out}" {}
-- OUTPUT --
Vertex: test[hello]

View File

@@ -15,6 +15,6 @@ $fn2 = $fn1()
$fn3 = $fn2()
$out = $fn3()
test $out {}
test "${out}" {}
-- OUTPUT --
Vertex: test[hello]

View File

@@ -33,10 +33,15 @@ $out2 = $fn1("there")
$out3 = $fn2("hello")
$out4 = $fn2("world")
test $out1() {} # hey
test $out2() {} # there
test $out3() {} # wow: hello
test $out4() {} # wow: world
$name1 = $out1()
$name2 = $out2()
$name3 = $out3()
$name4 = $out4()
test [$name1,] {} # hey
test [$name2,] {} # there
test [$name3,] {} # wow: hello
test [$name4,] {} # wow: world
-- OUTPUT --
Vertex: test[hey]
Vertex: test[there]

View File

@@ -5,6 +5,6 @@ $zero = 0
$one = $zero + 1
$two = $one + 1 # needs a chain to panic
test fmt.printf("%d%d%d", $zero, $one, $two) {}
test [fmt.printf("%d%d%d", $zero, $one, $two),] {}
-- OUTPUT --
Vertex: test[012]

View File

@@ -18,8 +18,8 @@ $fn2 = funcgen(false)
$out1 = $fn1()
$out2 = $fn2()
test $out1 {}
test $out2 {}
test "${out1}" {}
test "${out2}" {}
-- OUTPUT --
Vertex: test[hello]
Vertex: test[world]

View File

@@ -18,8 +18,8 @@ $fn2 = $funcgen(false)
$out1 = $fn1()
$out2 = $fn2()
test $out1 {}
test $out2 {}
test "${out1}" {}
test "${out2}" {}
-- OUTPUT --
Vertex: test[hello]
Vertex: test[world]

View File

@@ -36,10 +36,15 @@ $out2 = $fn1("world")
$out3 = $fn2("hello")
$out4 = $fn2("world")
test $out1() {}
test $out2() {}
test $out3() {}
test $out4() {}
$name1 = $out1()
$name2 = $out2()
$name3 = $out3()
$name4 = $out4()
test "${name1}" {}
test "${name2}" {}
test "${name3}" {}
test "${name4}" {}
-- OUTPUT --
Vertex: test[true-true]
Vertex: test[true-false]

View File

@@ -1,7 +1,7 @@
-- main.mcl --
$x1 = "t1"
class foo {
test $x1 {}
test "${x1}" {}
}
include foo
-- OUTPUT --

View File

@@ -1,7 +1,7 @@
-- main.mcl --
include foo
class foo {
test $x1 {}
test "${x1}" {}
}
$x1 = "t1"
-- OUTPUT --

View File

@@ -1,7 +1,7 @@
-- main.mcl --
$x1 = "bad1"
class foo($x1) {
test $x1 {}
test "${x1}" {}
}
include foo("t1")
-- OUTPUT --

View File

@@ -1,8 +1,8 @@
-- main.mcl --
$x1 = "t1"
class foo {
test $x1 {}
test $x2 {}
test "${x1}" {}
test "${x2}" {}
}
include foo
$x2 = "t2"

View File

@@ -1,8 +1,8 @@
-- main.mcl --
$x1 = "bad1"
class foo($x1, $x2) {
test "t1: " + $x2 {} # swapped
test "t2: " + $x1 {}
test ["t1: " + $x2,] {} # swapped
test ["t2: " + $x1,] {}
}
include foo($x2, "t1")
$x2 = "t2"

View File

@@ -2,10 +2,10 @@
$x1 = "bad1"
class foo($x1, $x2) {
include bar
test "t1: " + $x1 {}
test "t2: " + $x2 {}
test ["t1: " + $x1,] {}
test ["t2: " + $x2,] {}
class bar {
test "t0: " + $x0 {}
test ["t0: " + $x0,] {}
}
}
include foo("t1", $x2)

View File

@@ -6,7 +6,7 @@ include defs.foo
import "defs.mcl" # out of order for fun
-- defs.mcl --
class foo {
test $x1 {} # capture the var
test "${x1}" {} # capture the var
}
$x1 = "t1"
-- OUTPUT --

View File

@@ -10,8 +10,8 @@ class c1 {
include c1 as i1 # has $y
include i1.c0 as i0 # has $x ...and $y
test $i0.x {} # ok
test $i1.y {} # ok
test "${i0.x}" {} # ok
test "${i1.y}" {} # ok
panic($i0.x != "goodbye")
panic($i1.y != "hello")
@@ -21,7 +21,7 @@ panic($i1.y != "hello")
# current child scope, which makes this variable visible. Unfortunately, it does
# not have the correct dependency (edge) present in the Ordering system, so it
# is flaky depending on luck of the toposort.
#test $i0.y {}
#test "${i0.y}" {}
-- OUTPUT --
Vertex: test[goodbye]

View File

@@ -26,8 +26,11 @@ include i1.c0 as i01
include c1(false) as i2
include i2.c0 as i02
test $i01.x {}
test $i02.x {}
$name1 = $i01.x
$name2 = $i02.x
test "${name1}" {}
test "${name2}" {}
-- OUTPUT --
Vertex: test[hello]
Vertex: test[goodbye]

View File

@@ -6,6 +6,6 @@ class c1() {
# TODO: can this be allowed?
include c1 as i1
include c1 as i1
test $i1.x {}
test "${i1.x}" {}
-- OUTPUT --
# err: errSetScope: could not generate ordering: duplicate assignment to `scoped:i1`, have: include(c1)

View File

@@ -6,6 +6,6 @@ class c1($s) {
# TODO: can this be allowed?
include c1("hey") as i1
include c1("hey") as i1
test $i1.x {}
test "${i1.x}" {}
-- OUTPUT --
# err: errSetScope: could not generate ordering: duplicate assignment to `scoped:i1`, have: include(c1)

View File

@@ -5,6 +5,6 @@ class c1($s) {
include c1("hey") as i1
include c1("there") as i1
test $i1.x {}
test "${i1.x}" {}
-- OUTPUT --
# err: errSetScope: could not generate ordering: duplicate assignment to `scoped:i1`, have: include(c1)

View File

@@ -8,7 +8,7 @@ class c1 {
}
include c1 as i1
test i1.f1("world") {}
test [i1.f1("world"),] {}
-- OUTPUT --
Vertex: test[helloworld]
Vertex: test[t1]

View File

@@ -8,7 +8,7 @@ class c1 {
}
include c1 as i1
test i1.f1("whatever") {}
test [i1.f1("whatever"),] {}
-- OUTPUT --
Vertex: test[helloworld]
Vertex: test[t1]

View File

@@ -7,7 +7,7 @@ class c1 {
}
include c1 as i1
test i1.f1() {}
test [i1.f1(),] {}
-- OUTPUT --
Vertex: test[hello]
Vertex: test[t1]

View File

@@ -17,8 +17,8 @@ class c1($b) {
include c1(true) as i1
include c1(false) as i2
test i1.f1() {}
test i2.f1() {}
test [i1.f1(),] {}
test [i2.f1(),] {}
-- OUTPUT --
Vertex: test[hello]
Vertex: test[goodbye]

View File

@@ -27,10 +27,10 @@ class c1($b) {
include c1(true) as i1
include c1(false) as i2
test i1.f1() {}
test i2.f1() {}
test i1.i0.f0() {} # I think these might work directly too. Do we want them to?
test i2.i0.f0() {}
test [i1.f1(),] {}
test [i2.f1(),] {}
test [i1.i0.f0(),] {} # I think these might work directly too. Do we want them to?
test [i2.i0.f0(),] {}
-- OUTPUT --
Vertex: test[goodbye]
Vertex: test[hello]

View File

@@ -5,6 +5,6 @@ class c1($s) {
}
include c1("hey") as i1
test $i1.x {}
test "${i1.x}" {}
-- OUTPUT --
# err: errSetScope: could not generate ordering: duplicate assignment to `scoped:i1`, have: import(fmt)

View File

@@ -5,6 +5,6 @@ class c1($s) {
}
include c1("hey") as fmt
test $fmt.x {}
test "${fmt.x}" {}
-- OUTPUT --
# err: errSetScope: could not generate ordering: duplicate assignment to `scoped:fmt`, have: import(fmt)

View File

@@ -8,7 +8,9 @@ class c1 {
}
include c1 as i1
test $i1.f1("world") {}
$name = $i1.f1("world")
test "${name}" {}
-- OUTPUT --
Vertex: test[helloworld]
Vertex: test[t1]

View File

@@ -8,7 +8,9 @@ class c1 {
}
include c1 as i1
test $i1.f1("whatever") {}
$name = $i1.f1("whatever")
test "${name}" {}
-- OUTPUT --
Vertex: test[helloworld]
Vertex: test[t1]

View File

@@ -7,7 +7,9 @@ class c1 {
}
include c1 as i1
test $i1.f1() {}
$name = $i1.f1()
test "${name}" {}
-- OUTPUT --
Vertex: test[hello]
Vertex: test[t1]

View File

@@ -17,8 +17,11 @@ class c1($b) {
include c1(true) as i1
include c1(false) as i2
test $i1.f1() {}
test $i2.f1() {}
$name1 = $i1.f1()
$name2 = $i2.f1()
test "${name1}" {}
test "${name2}" {}
-- OUTPUT --
Vertex: test[hello]
Vertex: test[goodbye]

View File

@@ -28,11 +28,17 @@ class c1($b) {
include c1(true) as i1
include c1(false) as i2
test $i1.x() {}
test $i1.i0.f0() {}
test $i2.x() {}
test $i1.i0.f0() {} # I think these should work directly too. Do we want them to?
test $i2.i0.f0() {}
$name1 = $i1.x()
$name2 = $i1.i0.f0()
$name3 = $i2.x()
$name4 = $i1.i0.f0() # I think these should work directly too. Do we want them to?
$name5 = $i2.i0.f0()
test "${name1}" {}
test "${name2}" {}
test "${name3}" {}
test "${name4}" {}
test "${name5}" {}
-- OUTPUT --
Vertex: test[goodbye]
Vertex: test[hello]

View File

@@ -1,13 +1,13 @@
-- main.mcl --
$top = "top-level"
class base($s) {
test "middle " + $s {}
test ["middle " + $s,] {}
$middle = "inside base"
}
# syntactic sugar for the equivalent of defining a class `inner` inside of base.
class base:inner($s) {
test "inner " + $s {}
test ["inner " + $s,] {}
$last = "i am inner and i can see " + $middle
}
@@ -15,9 +15,9 @@ class base:inner($s) {
include base("world") as b1
include b1.inner("hello") as b2 # inner comes out of `base`
test $top {}
test $b1.middle {}
test $b2.last {}
test "${top}" {}
test "${b1.middle}" {}
test "${b2.last}" {}
-- OUTPUT --
Vertex: test[inner hello]
Vertex: test[middle world]

View File

@@ -1,26 +1,26 @@
-- main.mcl --
$top = "top-level"
class base($s) {
test "middle " + $s {}
test ["middle " + $s,] {}
$middle = "inside base"
}
# syntactic sugar for the equivalent of defining a class `inner` inside of base.
class base:inner1($s) {
test "inner1 " + $s {}
test ["inner1 " + $s,] {}
$last = "i am inner1 and i can see " + $middle
}
class base:inner2($s) {
test "inner2 " + $s {}
test ["inner2 " + $s,] {}
$last = "i am inner2 and i can see " + $middle
}
# three deep!
class base:inner1:deep($s, $b) {
test "deep is " + $s {}
test ["deep is " + $s,] {}
$end = "i am deep and i can see " + $middle + " and last says " + $last
}
@@ -30,11 +30,11 @@ include b0.inner1("hello") as b1 # inner comes out of `base`
include b0.inner2("hello") as b2 # inner comes out of `base`
include b1.deep("deep", true) as d # deep comes out of `inner1`
test $top {}
test $b0.middle {}
test $b1.last {}
test $b2.last {}
test $d.end {}
test "${top}" {}
test "${b0.middle}" {}
test "${b1.last}" {}
test "${b2.last}" {}
test "${d.end}" {}
-- OUTPUT --
Vertex: test[deep is deep]
Vertex: test[i am deep and i can see inside base and last says i am inner1 and i can see inside base]

View File

@@ -5,7 +5,7 @@ class c1 {
}
include c1 as i1
test $i1.x {}
test "${i1.x}" {}
-- OUTPUT --
Vertex: test[hello]
Vertex: test[t1]

View File

@@ -15,8 +15,8 @@ class c1($b) {
include c1(true) as i1
include c1(false) as i2
test $i1.x {}
test $i2.x {}
test "${i1.x}" {}
test "${i2.x}" {}
-- OUTPUT --
Vertex: test[hello]
Vertex: test[goodbye]

View File

@@ -25,10 +25,10 @@ class c1($b) {
include c1(true) as i1
include c1(false) as i2
test $i1.x {}
test $i2.x {}
test $i1.i0.y {} # I think these should work directly too. Do we want them to?
test $i2.i0.y {}
test "${i1.x}" {}
test "${i2.x}" {}
test "${i1.i0.y}" {} # I think these should work directly too. Do we want them to?
test "${i2.i0.y}" {}
-- OUTPUT --
Vertex: test[goodbye]
Vertex: test[hello]

View File

@@ -1,7 +1,7 @@
-- main.mcl --
$wat = "bad1"
class c1($wat) {
test $wat {}
test "${wat}" {}
}
include c1("hello")
-- OUTPUT --

View File

@@ -4,7 +4,7 @@ class shadowme($msg) {
$msg = "c"
if true {
$msg = "d" # this is used!
test $msg {}
test "${msg}" {}
}
}

View File

@@ -5,7 +5,7 @@ class shadowme($msg) {
if true {
$msg = "d"
}
test $msg {}
test "${msg}" {}
}
include shadowme("b")

View File

@@ -15,7 +15,7 @@ include c1(true)
# error prone, and also require a higher-order FRP, which would add complexity
# but little value.
test $foo {}
test "${foo}" {}
-- OUTPUT --
Vertex: test[a]
Vertex: test[hello]

View File

@@ -4,6 +4,7 @@ $f = func($x) {
$wat
}
test $f("foo") {}
$name = $f("foo")
test "${name}" {}
-- OUTPUT --
# err: errSetScope: variable x not in scope

View File

@@ -26,9 +26,9 @@ $out1 = $fn("user")
$out2 = $fn("james")
$out3 = $fn("")
test $out1 {}
test $out2 {}
test $out3 {}
test "${out1}" {}
test "${out2}" {}
test "${out3}" {}
-- OUTPUT --
Vertex: test[hello purpleidea]
Vertex: test[hello user]

View File

@@ -16,8 +16,10 @@ $generate = func($idn) {
$foo = iter.map([$id1, $id2,], $generate)
test $foo[0] || "fail" {}
test $foo[1] || "fail" {}
$name0 = $foo[0] || "fail"
$name1 = $foo[1] || "fail"
test "${name0}" {}
test "${name1}" {}
-- OUTPUT --
Vertex: test[foo]
Vertex: test[foofoo]

View File

@@ -24,8 +24,8 @@ $funcgen = func() {
}
}
test $out1 {}
test $out2 {}
test "${out1}" {}
test "${out2}" {}
-- OUTPUT --
Vertex: test[so true]
Vertex: test[so false]

View File

@@ -24,8 +24,8 @@ $fn = if $some_bool {
$out1 = $fn(true)
$out2 = $fn(false)
test $out1 {}
test $out2 {}
test "${out1}" {}
test "${out2}" {}
-- OUTPUT --
Vertex: test[so true]
Vertex: test[so false]

View File

@@ -1,5 +1,5 @@
-- main.mcl --
# this magic string variable should exist and be "exists"
test $const.res.file.state.exists {}
test "${const.res.file.state.exists}" {}
-- OUTPUT --
Vertex: test[exists]

View File

@@ -12,6 +12,7 @@ $id = func($y) { $y }
# since $apply changes over time, this call needs a dynamic sub-graph. In
# theory, the $f calls above do not need a sub-graph, but does our optimization
# support this corner case yet?
test $apply($id, "foo") {}
$name = $apply($id, "foo")
test "${name}" {}
-- OUTPUT --
Vertex: test[foo]

View File

@@ -32,10 +32,10 @@ $s4 = if contains([42, 13, 0,], $intlists) {
"passed4"
}
test $s1 {} # passed
test $s2 {} # passed
test $s3 {} # passed
test $s4 {} # passed
test "${s1}" {} # passed
test "${s2}" {} # passed
test "${s3}" {} # passed
test "${s4}" {} # passed
-- OUTPUT --
Vertex: test[passed1]
Vertex: test[passed2]

View File

@@ -12,8 +12,10 @@ $call_f1 = func($x) {
$call_f2 = func($x) {
$f2() + $x
}
test $call_f1("!") {}
test $call_f2("?") {}
$name1 = $call_f1("!")
$name2 = $call_f2("?")
test "${name1}" {}
test "${name2}" {}
-- OUTPUT --
Vertex: test[top-level1!]
Vertex: test[top-level2?]

View File

@@ -41,9 +41,9 @@ if $x3 != $mod1.x3 {
}
# hide the newlines from our output
test strings.trim_space($x1) {}
test strings.trim_space($x2) {}
test strings.trim_space($x3) {}
test [strings.trim_space($x1),] {}
test [strings.trim_space($x2),] {}
test [strings.trim_space($x3),] {}
# debugging:
#test "f1" {
# anotherstr => $x1,

View File

@@ -41,9 +41,9 @@ if $x3 != $mod1.x3 {
}
# hide the newlines from our output
test strings.trim_space($x1) {}
test strings.trim_space($x2) {}
test strings.trim_space($x3) {}
test [strings.trim_space($x1),] {}
test [strings.trim_space($x2),] {}
test [strings.trim_space($x3),] {}
# debugging:
#test "f1" {
# anotherstr => $x1,

View File

@@ -31,8 +31,8 @@ if $x2 != $second.x2 {
}
# hide the newlines from our output
test strings.trim_space($x1) {}
test strings.trim_space($x2) {}
test [strings.trim_space($x1),] {}
test [strings.trim_space($x2),] {}
-- second.mcl --
import "deploy"

View File

@@ -19,7 +19,7 @@ if $x1 != deploy.readfile($f1) {
}
# hide the newlines from our output
test strings.trim_space($x1) {}
test [strings.trim_space($x1),] {}
-- files/file1 --
This is file1 in the files/ folder.
-- OUTPUT --

View File

@@ -9,7 +9,7 @@ $f1 = "/files/file1"
$x1 = deploy.readfileabs($f1)
# hide the newlines from our output
test $x1 {}
test "${x1}" {}
-- files/file1 --
This is file1 in the files/ folder.
-- OUTPUT --

View File

@@ -8,8 +8,8 @@ func prefixer($x) {
$out1 = prefixer("a")
$out2 = prefixer("b")
test $out1 {} # hello:a
test $out2 {} # hello:b
test "${out1}" {} # hello:a
test "${out2}" {} # hello:b
-- OUTPUT --
Vertex: test[hello:a]
Vertex: test[hello:b]

View File

@@ -7,8 +7,8 @@ $prefixer = func($x) {
$out1 = $prefixer("a")
$out2 = $prefixer("b")
test $out1 {} # helloa
test $out2 {} # hellob
test "${out1}" {} # helloa
test "${out2}" {} # hellob
-- OUTPUT --
Vertex: test[helloa]
Vertex: test[hellob]

View File

@@ -0,0 +1,6 @@
-- main.mcl --
# This is an empty list of test resources, iow test resources. This must pass
# type unification. This previously only passed if we allowed recursive
# unification solving, but now we can support it without that.
test [] {}
-- OUTPUT --

View File

@@ -3,7 +3,7 @@ some comment can go here!
-- main.mcl --
import "fmt"
test fmt.printf("answer: %d", 42) {}
test [fmt.printf("answer: %d", 42),] {}
-- files/file1 --
this is file1
-- files/file2 --

View File

@@ -3,8 +3,8 @@ import "fmt"
import "math"
# FIXME: floats don't print nicely: https://github.com/golang/go/issues/46118
# FIXME: This means that we see "42" for both, instead of 42.0 ...
test fmt.printf("int: %d", math.fortytwo()) {}
test fmt.printf("float: %f", math.fortytwo()) {}
test [fmt.printf("int: %d", math.fortytwo()),] {}
test [fmt.printf("float: %f", math.fortytwo()),] {}
-- OUTPUT --
Vertex: test[float: 42]
Vertex: test[int: 42]

View File

@@ -6,6 +6,6 @@ func stradd($x) {
$x1 = stradd("hey")
test $x1 {}
test "${x1}" {}
-- OUTPUT --
Vertex: test[heyhey]

View File

@@ -9,6 +9,6 @@ class funcgen1 {
}
include funcgen1
$x1 = fun1() # not funcgen1.fun1 since it's *not* an import!
test $x1 {} # hi
test "${x1}" {} # hi
-- OUTPUT --
# err: errSetScope: func `fun1` does not exist in this scope

View File

@@ -12,6 +12,6 @@ $const2 = "world" # added here to confirm any-order rules
include funcgen2
$x2 = fun2() # not funcgen2.fun2 since it's *not* an import!
test $x2 {} # hello world
test "${x2}" {} # hello world
-- OUTPUT --
# err: errSetScope: func `fun2` does not exist in this scope

View File

@@ -10,6 +10,6 @@ func sq1($x) {
$x1 = sq1(3) # 3^2 + 4 = 13
test fmt.printf("sq1: %d", $x1) {}
test [fmt.printf("sq1: %d", $x1),] {}
-- OUTPUT --
Vertex: test[sq1: 6]

View File

@@ -11,7 +11,7 @@ func sq1($x) {
$x1 = sq1(math.fortytwo())
test fmt.printf("sq1: %d", $x1) {}
test [fmt.printf("sq1: %d", $x1),] {}
-- OUTPUT --
Vertex: test[sq1: 45]

View File

@@ -8,6 +8,6 @@ func stradd($x) {
$x1 = stradd("hey")
test $x1 {}
test "${x1}" {}
-- OUTPUT --
Vertex: test[heythere]

View File

@@ -3,6 +3,6 @@ $foo = "bad1"
func bar($foo) {
"hello " + $foo # shadows parent var
}
test bar("world") {}
test [bar("world"),] {}
-- OUTPUT --
Vertex: test[hello world]

View File

@@ -3,6 +3,7 @@ $foo = "bad1"
$bar = func($foo) {
"hello " + $foo # shadows parent var
}
test $bar("world") {}
$name = $bar("world")
test "${name}" {}
-- OUTPUT --
Vertex: test[hello world]

View File

@@ -8,6 +8,6 @@ func stradd($x) {
$x1 = stradd("hey")
test $x1 {}
test "${x1}" {}
-- OUTPUT --
Vertex: test[nothing]

View File

@@ -31,15 +31,16 @@ $funcgen3 = func() {
$fn1 = $funcgen1()
$out1 = $fn1("hello")
test $out1 {}
test "${out1}" {}
$fn2 = $funcgen2()
$out2 = $fn2("hello")
test $out2 {}
test "${out2}" {}
$fn3 = $funcgen3()
$out3 = $fn3("goodbye")
test $out3() {}
$name3 = $out3()
test "${name3}" {}
-- OUTPUT --
Vertex: test[goodbye]
Vertex: test[hello world]

View File

@@ -4,6 +4,6 @@ func identity($x) {
$x
}
test identity("hey") {}
test [identity("hey"),] {}
-- OUTPUT --
Vertex: test[hey]

View File

@@ -5,6 +5,6 @@ $out1 = if true {
"world"
}
test $out1 {}
test "${out1}" {}
-- OUTPUT --
Vertex: test[hello]

View File

@@ -10,6 +10,6 @@ $fn = func($b) {
$out1 = $fn(true)
test $out1 {}
test "${out1}" {}
-- OUTPUT --
Vertex: test[hello]

View File

@@ -13,9 +13,9 @@ class c2() {
include c2 as f1
test $f1.z {} # yep
#test $f1.x {} # no
test $f1.g1.x {} # yep
test "${f1.z}" {} # yep
#test "${f1.x}" {} # no
test "${f1.g1.x}" {} # yep
-- OUTPUT --
Vertex: test[i am y and i am x]

View File

@@ -10,9 +10,9 @@ class c2() {
include c2 as f1
test $f1.z {}
test $f1.x {} # tricky
test $f1.newx {}
test "${f1.z}" {}
test "${f1.x}" {} # tricky
test "${f1.newx}" {}
-- OUTPUT --
Vertex: test[i am x]

View File

@@ -12,9 +12,9 @@ class c2() {
include c2 as f1
test $f1.z {}
test $f1.x {} # tricky
test $f1.newx {}
test "${f1.z}" {}
test "${f1.x}" {} # tricky
test "${f1.newx}" {}
-- OUTPUT --
# err: errSetScope: recursive reference while setting scope: not a dag

View File

@@ -10,8 +10,8 @@ class c2() {
include c2 as f1
test $f1.z {}
test $f1.x1 {}
test "${f1.z}" {}
test "${f1.x1}" {}
# the really tricky case
# XXX: works atm, but not supported for now: could not set scope: variable f1.x2 not in scope
@@ -19,7 +19,7 @@ test $f1.x1 {}
# current child scope, which makes this variable visible. Unfortunately, it does
# not have the correct dependency (edge) present in the Ordering system, so it
# is flaky depending on luck of the toposort.
#test $f1.x2 {}
#test "${f1.x2}" {}
-- OUTPUT --
Vertex: test[hey]

View File

@@ -19,12 +19,12 @@ class c2() {
include c2 as f1
test $x {}
test $f1.y {}
test "${x}" {}
test "${f1.y}" {}
# the really tricky case
# XXX: not supported for now: could not set scope: not a dag
#test $f1.x {}
#test "${f1.x}" {}
-- OUTPUT --
Vertex: test[i am x]

View File

@@ -12,9 +12,9 @@ class c2() {
include c2 as f1
test $f1.z {}
test $f1.x {} # tricky
test $f1.newx {}
test "${f1.z}" {}
test "${f1.x}" {} # tricky
test "${f1.newx}" {}
-- OUTPUT --
# err: errSetScope: recursive reference while setting scope: not a dag

Some files were not shown because too many files have changed in this diff Show More