lang: Add a for loop statement for iterating over a list
This adds a for statement which is used to iterate over a list with a body of statements. This is an important data transformation tool which should be used sparingly, but is important to have. An import statement inside of a for loop is not currently supported. We have a simple hack to detect the obvious cases, but more deeply nested scenarios probably won't be caught, and you'll get an obscure error message if you try to do this. This was incredibly challenging to get right, and it's all thanks to Sam for his brilliance. Co-authored-by: Samuel Gélineau <gelisam@gmail.com>
This commit is contained in:
@@ -100,6 +100,15 @@ expression
|
||||
}
|
||||
```
|
||||
|
||||
- **for**: loop over a list with a body of statements
|
||||
|
||||
```mcl
|
||||
$list = ["a", "b", "c",]
|
||||
for $index, $value in $list {
|
||||
# some statements go here
|
||||
}
|
||||
```
|
||||
|
||||
- **resource**: produces a resource
|
||||
|
||||
```mcl
|
||||
|
||||
39
examples/lang/for.mcl
Normal file
39
examples/lang/for.mcl
Normal file
@@ -0,0 +1,39 @@
|
||||
import "datetime"
|
||||
import "fmt"
|
||||
import "math"
|
||||
|
||||
$now = datetime.now()
|
||||
$alpha = if math.mod($now, 2) == 0 {
|
||||
"m"
|
||||
} else {
|
||||
"j"
|
||||
}
|
||||
|
||||
$list0 = ["a", "b", "c",]
|
||||
$list1 = ["d", "e", "f",]
|
||||
$list2 = ["g", "h", "i",]
|
||||
$list3 = ["${alpha}", "k", "l",]
|
||||
|
||||
$list = [$list0, $list1, $list2, $list3,]
|
||||
|
||||
for $index, $value in $list {
|
||||
for $i, $v in $value {
|
||||
$s = fmt.printf("%s is %d", $v, $i+$index)
|
||||
print [$s,] {
|
||||
Meta:autogroup => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Vertex: test[a is 0]
|
||||
#Vertex: test[b is 1]
|
||||
#Vertex: test[c is 2]
|
||||
#Vertex: test[d is 1]
|
||||
#Vertex: test[e is 2]
|
||||
#Vertex: test[f is 3]
|
||||
#Vertex: test[g is 2]
|
||||
#Vertex: test[h is 3]
|
||||
#Vertex: test[i is 4]
|
||||
#Vertex: test[j is 3]
|
||||
#Vertex: test[k is 4]
|
||||
#Vertex: test[l is 5]
|
||||
@@ -73,6 +73,11 @@ func (obj *StmtIf) ScopeGraph(g *pgraph.Graph) {
|
||||
g.AddVertex(obj)
|
||||
}
|
||||
|
||||
// ScopeGraph adds nodes and vertices to the supplied graph.
|
||||
func (obj *StmtFor) ScopeGraph(g *pgraph.Graph) {
|
||||
g.AddVertex(obj)
|
||||
}
|
||||
|
||||
// ScopeGraph adds nodes and vertices to the supplied graph.
|
||||
func (obj *StmtProg) ScopeGraph(g *pgraph.Graph) {
|
||||
g.AddVertex(obj)
|
||||
@@ -196,6 +201,17 @@ func (obj *ExprParam) ScopeGraph(g *pgraph.Graph) {
|
||||
g.AddVertex(obj)
|
||||
}
|
||||
|
||||
// ScopeGraph adds nodes and vertices to the supplied graph.
|
||||
func (obj *ExprIterated) ScopeGraph(g *pgraph.Graph) {
|
||||
g.AddVertex(obj)
|
||||
definition, ok := obj.Definition.(interfaces.ScopeGrapher)
|
||||
if !ok {
|
||||
panic("can't graph scope") // programming error
|
||||
}
|
||||
definition.ScopeGraph(g)
|
||||
g.AddEdge(obj, obj.Definition, &pgraph.SimpleEdge{Name: "def"})
|
||||
}
|
||||
|
||||
// ScopeGraph adds nodes and vertices to the supplied graph.
|
||||
func (obj *ExprPoly) ScopeGraph(g *pgraph.Graph) {
|
||||
g.AddVertex(obj)
|
||||
|
||||
1352
lang/ast/structs.go
1352
lang/ast/structs.go
File diff suppressed because it is too large
Load Diff
@@ -339,11 +339,54 @@ func trueCallee(apparentCallee interfaces.Expr) interfaces.Expr {
|
||||
return trueCallee(x.Definition)
|
||||
case *ExprSingleton:
|
||||
return trueCallee(x.Definition)
|
||||
case *ExprIterated:
|
||||
return trueCallee(x.Definition)
|
||||
case *ExprPoly: // XXX: Did we want this one added too?
|
||||
return trueCallee(x.Definition)
|
||||
|
||||
default:
|
||||
return apparentCallee
|
||||
}
|
||||
}
|
||||
|
||||
// findExprPoly is a helper used in SetScope.
|
||||
func findExprPoly(apparentCallee interfaces.Expr) *ExprPoly {
|
||||
switch x := apparentCallee.(type) {
|
||||
case *ExprTopLevel:
|
||||
return findExprPoly(x.Definition)
|
||||
case *ExprSingleton:
|
||||
return findExprPoly(x.Definition)
|
||||
case *ExprIterated:
|
||||
return findExprPoly(x.Definition)
|
||||
case *ExprPoly:
|
||||
return x // found it!
|
||||
default:
|
||||
return nil // not found!
|
||||
}
|
||||
}
|
||||
|
||||
// newExprParam is a helper function to create an ExprParam with the internal
|
||||
// key set to the pointer of the thing we're creating.
|
||||
func newExprParam(name string, typ *types.Type) *ExprParam {
|
||||
expr := &ExprParam{
|
||||
Name: name,
|
||||
typ: typ,
|
||||
}
|
||||
expr.envKey = expr
|
||||
return expr
|
||||
}
|
||||
|
||||
// newExprIterated is a helper function to create an ExprIterated with the
|
||||
// internal key set to the pointer of the thing we're creating.
|
||||
func newExprIterated(name string, definition interfaces.Expr) *ExprIterated {
|
||||
expr := &ExprIterated{
|
||||
Name: name,
|
||||
Definition: definition,
|
||||
}
|
||||
expr.envKey = expr
|
||||
return expr
|
||||
}
|
||||
|
||||
// variableScopeFeedback logs some messages about what is actually in scope so
|
||||
// that the user gets a hint about what's going on. This is useful for catching
|
||||
// bugs in our programming or in user code!
|
||||
|
||||
@@ -1240,6 +1240,8 @@ func (obj *Engine) Run(ctx context.Context) (reterr error) {
|
||||
}
|
||||
|
||||
fn := func(nodeCtx context.Context) (reterr error) {
|
||||
// NOTE: Comment out this defer to make
|
||||
// debugging a lot easier.
|
||||
defer func() {
|
||||
// catch programming errors
|
||||
if r := recover(); r != nil {
|
||||
|
||||
254
lang/funcs/structs/for.go
Normal file
254
lang/funcs/structs/for.go
Normal file
@@ -0,0 +1,254 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
package structs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/purpleidea/mgmt/lang/interfaces"
|
||||
"github.com/purpleidea/mgmt/lang/types"
|
||||
"github.com/purpleidea/mgmt/util/errwrap"
|
||||
)
|
||||
|
||||
const (
|
||||
// ForFuncName is the unique name identifier for this function.
|
||||
ForFuncName = "for"
|
||||
|
||||
// ForFuncArgNameList is the name for the edge which connects the input
|
||||
// list to CallFunc.
|
||||
ForFuncArgNameList = "list"
|
||||
)
|
||||
|
||||
// ForFunc receives a list from upstream. We iterate over the received list to
|
||||
// build a subgraph that processes each element, and in doing so we get a larger
|
||||
// function graph. This is rebuilt as necessary if the input list changes.
|
||||
type ForFunc struct {
|
||||
IndexType *types.Type
|
||||
ValueType *types.Type
|
||||
|
||||
EdgeName string // name of the edge used
|
||||
|
||||
AppendToIterBody func(innerTxn interfaces.Txn, index int, value interfaces.Func) error
|
||||
ClearIterBody func(length int)
|
||||
|
||||
init *interfaces.Init
|
||||
|
||||
lastInputListLength int // remember the last input list length
|
||||
}
|
||||
|
||||
// String returns a simple name for this function. This is needed so this struct
|
||||
// can satisfy the pgraph.Vertex interface.
|
||||
func (obj *ForFunc) String() string {
|
||||
return ForFuncName
|
||||
}
|
||||
|
||||
// Validate makes sure we've built our struct properly.
|
||||
func (obj *ForFunc) Validate() error {
|
||||
if obj.IndexType == nil {
|
||||
return fmt.Errorf("must specify a type")
|
||||
}
|
||||
if obj.ValueType == nil {
|
||||
return fmt.Errorf("must specify a type")
|
||||
}
|
||||
|
||||
// TODO: maybe we can remove this if we use this for core functions...
|
||||
if obj.EdgeName == "" {
|
||||
return fmt.Errorf("must specify an edge name")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Info returns some static info about itself.
|
||||
func (obj *ForFunc) Info() *interfaces.Info {
|
||||
var typ *types.Type
|
||||
|
||||
if obj.IndexType != nil && obj.ValueType != nil { // don't panic if called speculatively
|
||||
// XXX: Improve function engine so it can return no value?
|
||||
//typ = types.NewType(fmt.Sprintf("func(%s []%s)", obj.EdgeName, obj.ValueType)) // returns nothing
|
||||
// XXX: Temporary float type to prove we're dropping the output since we don't use it.
|
||||
typ = types.NewType(fmt.Sprintf("func(%s []%s) float", obj.EdgeName, obj.ValueType))
|
||||
}
|
||||
|
||||
return &interfaces.Info{
|
||||
Pure: true,
|
||||
Memo: false, // TODO: ???
|
||||
Sig: typ,
|
||||
Err: obj.Validate(),
|
||||
}
|
||||
}
|
||||
|
||||
// Init runs some startup code for this composite function.
|
||||
func (obj *ForFunc) Init(init *interfaces.Init) error {
|
||||
obj.init = init
|
||||
obj.lastInputListLength = -1
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stream takes an input struct in the format as described in the Func and Graph
|
||||
// methods of the Expr, and returns the actual expected value as a stream based
|
||||
// on the changing inputs to that value.
|
||||
func (obj *ForFunc) Stream(ctx context.Context) error {
|
||||
defer close(obj.init.Output) // the sender closes
|
||||
|
||||
// A Func to send input lists to the subgraph. The Txn.Erase() call
|
||||
// ensures that this Func is not removed when the subgraph is recreated,
|
||||
// so that the function graph can propagate the last list we received to
|
||||
// the subgraph.
|
||||
inputChan := make(chan types.Value)
|
||||
subgraphInput := &ChannelBasedSourceFunc{
|
||||
Name: "subgraphInput",
|
||||
Source: obj,
|
||||
Chan: inputChan,
|
||||
Type: obj.listType(),
|
||||
}
|
||||
obj.init.Txn.AddVertex(subgraphInput)
|
||||
if err := obj.init.Txn.Commit(); err != nil {
|
||||
return errwrap.Wrapf(err, "commit error in Stream")
|
||||
}
|
||||
obj.init.Txn.Erase() // prevent the next Reverse() from removing subgraphInput
|
||||
defer func() {
|
||||
close(inputChan)
|
||||
obj.init.Txn.Reverse()
|
||||
obj.init.Txn.DeleteVertex(subgraphInput)
|
||||
obj.init.Txn.Commit()
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case input, ok := <-obj.init.Input:
|
||||
if !ok {
|
||||
obj.init.Input = nil // block looping back here
|
||||
//canReceiveMoreListValues = false
|
||||
// We don't ever shutdown here, since even if we
|
||||
// don't get more lists, that last list value is
|
||||
// still propagating inside of the subgraph and
|
||||
// so we don't want to shutdown since that would
|
||||
// reverse the txn which we only do at the very
|
||||
// end on graph shutdown.
|
||||
continue
|
||||
}
|
||||
|
||||
forList, exists := input.Struct()[obj.EdgeName]
|
||||
if !exists {
|
||||
return fmt.Errorf("programming error, can't find edge")
|
||||
}
|
||||
|
||||
// If the length of the input list has changed, then we
|
||||
// need to replace the subgraph with a new one that has
|
||||
// that many "tentacles". Basically the shape of the
|
||||
// graph depends on the length of the list. If we get a
|
||||
// brand new list where each value is different, but
|
||||
// the length is the same, then we can just flow new
|
||||
// values into the list and we don't need to change the
|
||||
// graph shape! Changing the graph shape is more
|
||||
// expensive, so we don't do it when not necessary.
|
||||
n := len(forList.List())
|
||||
|
||||
//if forList.Cmp(obj.lastForList) != nil // don't!
|
||||
if n != obj.lastInputListLength {
|
||||
//obj.lastForList = forList
|
||||
obj.lastInputListLength = n
|
||||
// replaceSubGraph uses the above two values
|
||||
if err := obj.replaceSubGraph(subgraphInput); err != nil {
|
||||
return errwrap.Wrapf(err, "could not replace subgraph")
|
||||
}
|
||||
}
|
||||
|
||||
// send the new input list to the subgraph
|
||||
select {
|
||||
case inputChan <- forList:
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
|
||||
select {
|
||||
case obj.init.Output <- &types.FloatValue{
|
||||
V: 42.0, // XXX: temporary
|
||||
}:
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (obj *ForFunc) replaceSubGraph(subgraphInput interfaces.Func) error {
|
||||
// delete the old subgraph
|
||||
if err := obj.init.Txn.Reverse(); err != nil {
|
||||
return errwrap.Wrapf(err, "could not Reverse")
|
||||
}
|
||||
|
||||
obj.ClearIterBody(obj.lastInputListLength) // XXX: pass in size?
|
||||
|
||||
for i := 0; i < obj.lastInputListLength; i++ {
|
||||
i := i
|
||||
argName := "forInputList"
|
||||
|
||||
inputElemFunc := SimpleFnToDirectFunc(
|
||||
fmt.Sprintf("forInputElem[%d]", i),
|
||||
&types.FuncValue{
|
||||
V: func(_ context.Context, args []types.Value) (types.Value, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, fmt.Errorf("inputElemFunc: expected a single argument")
|
||||
}
|
||||
arg := args[0]
|
||||
|
||||
list, ok := arg.(*types.ListValue)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("inputElemFunc: expected a ListValue argument")
|
||||
}
|
||||
|
||||
return list.List()[i], nil
|
||||
},
|
||||
T: types.NewType(fmt.Sprintf("func(%s %s) %s", argName, obj.listType(), obj.ValueType)),
|
||||
},
|
||||
)
|
||||
obj.init.Txn.AddVertex(inputElemFunc)
|
||||
|
||||
obj.init.Txn.AddEdge(subgraphInput, inputElemFunc, &interfaces.FuncEdge{
|
||||
Args: []string{argName},
|
||||
})
|
||||
|
||||
if err := obj.AppendToIterBody(obj.init.Txn, i, inputElemFunc); err != nil {
|
||||
return errwrap.Wrapf(err, "could not call AppendToIterBody()")
|
||||
}
|
||||
}
|
||||
|
||||
return obj.init.Txn.Commit()
|
||||
}
|
||||
|
||||
func (obj *ForFunc) listType() *types.Type {
|
||||
return types.NewType(fmt.Sprintf("[]%s", obj.ValueType))
|
||||
}
|
||||
@@ -206,8 +206,14 @@ func (obj *opAddEdge) Fn(opapi *opapi) error {
|
||||
args[x] = struct{}{}
|
||||
}
|
||||
if len(args) != len(obj.FE.Args)+len(edge.Args) {
|
||||
// programming error
|
||||
return fmt.Errorf("duplicate arg found")
|
||||
// previously, a programming error
|
||||
// On 24/nov/2024, Sam and I agreed this should be on.
|
||||
// On 11/jan/2025, Sam and I decided to disable this
|
||||
// check, since it was cause duplicates when a nested
|
||||
// StmtFor was having it's list contents add more than
|
||||
// once. It may be helpful to turn this on when
|
||||
// debugging graph transactions not involved StmtFor.
|
||||
//return fmt.Errorf("duplicate arg found: %v -> %v (%v)", obj.F1, obj.F2, obj.FE.Args)
|
||||
}
|
||||
newArgs := []string{}
|
||||
for x := range args {
|
||||
|
||||
@@ -85,8 +85,9 @@ type Stmt interface {
|
||||
// child statements, and Infer/Check for child expressions.
|
||||
TypeCheck() ([]*UnificationInvariant, error)
|
||||
|
||||
// Graph returns the reactive function graph expressed by this node.
|
||||
Graph() (*pgraph.Graph, error)
|
||||
// Graph returns the reactive function graph expressed by this node. It
|
||||
// takes in the environment of any functions in scope.
|
||||
Graph(env *Env) (*pgraph.Graph, error)
|
||||
|
||||
// Output returns the output that this "program" produces. This output
|
||||
// is what is used to build the output graph. It requires the input
|
||||
@@ -148,7 +149,7 @@ type Expr interface {
|
||||
// Graph returns the reactive function graph expressed by this node. It
|
||||
// takes in the environment of any functions in scope. It also returns
|
||||
// the function for this node.
|
||||
Graph(env map[string]Func) (*pgraph.Graph, Func, error)
|
||||
Graph(env *Env) (*pgraph.Graph, Func, error)
|
||||
|
||||
// SetValue stores the result of the last computation of this expression
|
||||
// node.
|
||||
@@ -277,10 +278,22 @@ func (obj *Data) AbsFilename() string {
|
||||
// An interesting note about these is that they exist in a distinct namespace
|
||||
// from the variables, which could actually contain lambda functions.
|
||||
type Scope struct {
|
||||
// Variables maps the scope of name to Expr.
|
||||
Variables map[string]Expr
|
||||
Functions map[string]Expr // the Expr will usually be an *ExprFunc (actually it's usually (or always) an *ExprSingleton, which wraps an *ExprFunc now)
|
||||
|
||||
// Functions is the scope of functions.
|
||||
//
|
||||
// The Expr will usually be an *ExprFunc. (Actually it's usually or
|
||||
// always an *ExprSingleton, which wraps an *ExprFunc now.)
|
||||
Functions map[string]Expr
|
||||
|
||||
// Classes map the name of the class to the class.
|
||||
Classes map[string]Stmt
|
||||
|
||||
// Iterated is a flag that is true if this scope is inside of a for
|
||||
// loop.
|
||||
Iterated bool
|
||||
|
||||
Chain []Node // chain of previously seen node's
|
||||
}
|
||||
|
||||
@@ -291,6 +304,7 @@ func EmptyScope() *Scope {
|
||||
Variables: make(map[string]Expr),
|
||||
Functions: make(map[string]Expr),
|
||||
Classes: make(map[string]Stmt),
|
||||
Iterated: false,
|
||||
Chain: []Node{},
|
||||
}
|
||||
}
|
||||
@@ -307,6 +321,7 @@ func (obj *Scope) Copy() *Scope {
|
||||
variables := make(map[string]Expr)
|
||||
functions := make(map[string]Expr)
|
||||
classes := make(map[string]Stmt)
|
||||
iterated := obj.Iterated
|
||||
chain := []Node{}
|
||||
|
||||
for k, v := range obj.Variables { // copy
|
||||
@@ -326,6 +341,7 @@ func (obj *Scope) Copy() *Scope {
|
||||
Variables: variables,
|
||||
Functions: functions,
|
||||
Classes: classes,
|
||||
Iterated: iterated,
|
||||
Chain: chain,
|
||||
}
|
||||
}
|
||||
@@ -377,6 +393,10 @@ func (obj *Scope) Merge(scope *Scope) error {
|
||||
obj.Classes[name] = scope.Classes[name]
|
||||
}
|
||||
|
||||
if scope.Iterated { // XXX: how should we merge this?
|
||||
obj.Iterated = scope.Iterated
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -398,6 +418,94 @@ func (obj *Scope) IsEmpty() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Env is an environment which contains the relevant mappings. This is used at
|
||||
// the Graph(...) stage of the compiler. It does not contain classes.
|
||||
type Env struct {
|
||||
// Variables map and Expr to a *FuncSingleton which deduplicates the
|
||||
// use of a function.
|
||||
Variables map[Expr]*FuncSingleton
|
||||
|
||||
// Functions contains the captured environment, because when we're
|
||||
// recursing into a StmtFunc which is defined inside a for loop, we can
|
||||
// use that to get the right Env.Variables map. As for the function
|
||||
// itself, it's the same in each loop iteration, therefore, we find it
|
||||
// in obj.expr of ExprCall. (Functions map[string]*Env) But actually,
|
||||
// our new version is now this:
|
||||
Functions map[Expr]*Env
|
||||
}
|
||||
|
||||
// EmptyEnv returns the zero, empty value for the scope, with all the internal
|
||||
// lists initialized appropriately.
|
||||
func EmptyEnv() *Env {
|
||||
return &Env{
|
||||
Variables: make(map[Expr]*FuncSingleton),
|
||||
Functions: make(map[Expr]*Env),
|
||||
}
|
||||
}
|
||||
|
||||
// Copy makes a copy of the Env struct. This ensures that if the internal maps
|
||||
// are changed, it doesn't affect other copies of the Env. It does *not* copy or
|
||||
// change the pointers contained within, since these are references, and we need
|
||||
// those to be consistently pointing to the same things after copying.
|
||||
func (obj *Env) Copy() *Env {
|
||||
if obj == nil { // allow copying nil envs
|
||||
return EmptyEnv()
|
||||
}
|
||||
|
||||
variables := make(map[Expr]*FuncSingleton)
|
||||
functions := make(map[Expr]*Env)
|
||||
|
||||
for k, v := range obj.Variables { // copy
|
||||
variables[k] = v // we don't copy the func's!
|
||||
}
|
||||
for k, v := range obj.Functions { // copy
|
||||
functions[k] = v // we don't copy the generator func's
|
||||
}
|
||||
|
||||
return &Env{
|
||||
Variables: variables,
|
||||
Functions: functions,
|
||||
}
|
||||
}
|
||||
|
||||
// FuncSingleton is a singleton system for storing a singleton func and its
|
||||
// corresponding graph. You must pass in a `MakeFunc` builder method to generate
|
||||
// these. The graph which is returned from this must contain that Func as a
|
||||
// node.
|
||||
type FuncSingleton struct {
|
||||
// MakeFunc builds and returns a Func and a graph that it must be
|
||||
// contained within.
|
||||
// XXX: Add Txn as an input arg?
|
||||
MakeFunc func() (*pgraph.Graph, Func, error)
|
||||
|
||||
g *pgraph.Graph
|
||||
f Func
|
||||
}
|
||||
|
||||
// GraphFunc returns the previously saved graph and func if they exist. If they
|
||||
// do not, then it calls the MakeFunc method to get them, and saves a copy for
|
||||
// next time.
|
||||
// XXX: Add Txn as an input arg?
|
||||
func (obj *FuncSingleton) GraphFunc() (*pgraph.Graph, Func, error) {
|
||||
// If obj.f already exists, just use that.
|
||||
if obj.f != nil { // && obj.g != nil
|
||||
return obj.g, obj.f, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
obj.g, obj.f, err = obj.MakeFunc() // XXX: Add Txn as an input arg?
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if obj.g == nil {
|
||||
return nil, nil, fmt.Errorf("unexpected nil graph")
|
||||
}
|
||||
if obj.f == nil {
|
||||
return nil, nil, fmt.Errorf("unexpected nil function")
|
||||
}
|
||||
return obj.g, obj.f, nil
|
||||
}
|
||||
|
||||
// Arg represents a name identifier for a func or class argument declaration and
|
||||
// is sometimes accompanied by a type. This does not satisfy the Expr interface.
|
||||
type Arg struct {
|
||||
|
||||
@@ -507,7 +507,7 @@ func TestAstFunc1(t *testing.T) {
|
||||
}
|
||||
|
||||
// build the function graph
|
||||
fgraph, err := iast.Graph()
|
||||
fgraph, err := iast.Graph(interfaces.EmptyEnv()) // XXX: Ask Sam
|
||||
if (!fail || !failGraph) && err != nil {
|
||||
t.Errorf("test #%d: FAIL", index)
|
||||
t.Errorf("test #%d: functions failed with: %+v", index, err)
|
||||
@@ -1101,8 +1101,29 @@ func TestAstFunc2(t *testing.T) {
|
||||
// in implementation and before unification, and static
|
||||
// once we've unified the specific resource.
|
||||
|
||||
// build the env
|
||||
//fgraph := &pgraph.Graph{Name: "functionGraph"}
|
||||
env := interfaces.EmptyEnv()
|
||||
// XXX: Do we need to do something like this?
|
||||
//for k, v := range scope.Variables {
|
||||
// g, builtinFunc, err := v.Graph(nil)
|
||||
// if err != nil {
|
||||
// t.Errorf("test #%d: FAIL", index)
|
||||
// t.Errorf("test #%d: calling Graph on builtins errored: %+v", index, err)
|
||||
// return
|
||||
// }
|
||||
// fgraph.AddGraph(g)
|
||||
// env.Variables[k] = builtinFunc // XXX: Ask Sam (.Functions ???)
|
||||
//}
|
||||
//for k, closure := range scope.Functions {
|
||||
// env.Functions[k] = &interfaces.Closure{
|
||||
// Env: interfaces.EmptyEnv(),
|
||||
// Expr: closure.Expr, // XXX: Ask Sam
|
||||
// }
|
||||
//}
|
||||
|
||||
// build the function graph
|
||||
fgraph, err := iast.Graph()
|
||||
fgraph, err := iast.Graph(env) // XXX: Ask Sam
|
||||
if (!fail || !failGraph) && err != nil {
|
||||
t.Errorf("test #%d: FAIL", index)
|
||||
t.Errorf("test #%d: functions failed with: %+v", index, err)
|
||||
@@ -1931,7 +1952,7 @@ func TestAstFunc3(t *testing.T) {
|
||||
// once we've unified the specific resource.
|
||||
|
||||
// build the function graph
|
||||
fgraph, err := iast.Graph()
|
||||
fgraph, err := iast.Graph(interfaces.EmptyEnv()) // XXX: Ask Sam
|
||||
if (!fail || !failGraph) && err != nil {
|
||||
t.Errorf("test #%d: FAIL", index)
|
||||
t.Errorf("test #%d: functions failed with: %+v", index, err)
|
||||
|
||||
@@ -17,4 +17,4 @@ $out2 = $add($val) # hellohello
|
||||
|
||||
test [fmt.printf("%s + %s is %s", $val, $val, $out2),] {} # simple concat
|
||||
-- OUTPUT --
|
||||
# err: errUnify: unify error with: topLevel(singleton(func(x) { call:_operator(str("+"), var(x), var(x)) })): type error: int != str
|
||||
# err: errUnify: unify error with: topLevel(singleton(func(x) { call:_operator(str("+"), var(x), var(x)) })): type error: str != int
|
||||
|
||||
13
lang/interpret_test/TestAstFunc2/stmtfor00.txtar
Normal file
13
lang/interpret_test/TestAstFunc2/stmtfor00.txtar
Normal file
@@ -0,0 +1,13 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
for $index, $value in $list {
|
||||
test [$value,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[a]
|
||||
Vertex: test[b]
|
||||
Vertex: test[c]
|
||||
15
lang/interpret_test/TestAstFunc2/stmtfor01.txtar
Normal file
15
lang/interpret_test/TestAstFunc2/stmtfor01.txtar
Normal file
@@ -0,0 +1,15 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
for $index, $value in $list {
|
||||
$s = $value # our first major bug was triggered by this!
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
# The buggy version would return "test[a]" three times!
|
||||
-- OUTPUT --
|
||||
Vertex: test[a]
|
||||
Vertex: test[b]
|
||||
Vertex: test[c]
|
||||
13
lang/interpret_test/TestAstFunc2/stmtfor02.txtar
Normal file
13
lang/interpret_test/TestAstFunc2/stmtfor02.txtar
Normal file
@@ -0,0 +1,13 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
for $index, $value in $list {
|
||||
test [fmt.printf("%s is %d", $value, $index),] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[a is 0]
|
||||
Vertex: test[b is 1]
|
||||
Vertex: test[c is 2]
|
||||
14
lang/interpret_test/TestAstFunc2/stmtfor03.txtar
Normal file
14
lang/interpret_test/TestAstFunc2/stmtfor03.txtar
Normal file
@@ -0,0 +1,14 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
for $index, $value in $list {
|
||||
$s = fmt.printf("%s is %d", $value, $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[a is 0]
|
||||
Vertex: test[b is 1]
|
||||
Vertex: test[c is 2]
|
||||
16
lang/interpret_test/TestAstFunc2/stmtfor04.txtar
Normal file
16
lang/interpret_test/TestAstFunc2/stmtfor04.txtar
Normal file
@@ -0,0 +1,16 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$s = "nope" # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
$s = fmt.printf("%s is %d", $value, $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[a is 0]
|
||||
Vertex: test[b is 1]
|
||||
Vertex: test[c is 2]
|
||||
16
lang/interpret_test/TestAstFunc2/stmtfor05.txtar
Normal file
16
lang/interpret_test/TestAstFunc2/stmtfor05.txtar
Normal file
@@ -0,0 +1,16 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$value = "nope" # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
$s = fmt.printf("%s is %d", $value, $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[a is 0]
|
||||
Vertex: test[b is 1]
|
||||
Vertex: test[c is 2]
|
||||
16
lang/interpret_test/TestAstFunc2/stmtfor06.txtar
Normal file
16
lang/interpret_test/TestAstFunc2/stmtfor06.txtar
Normal file
@@ -0,0 +1,16 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$value = 42 # should be out of scope (also not the same type)
|
||||
|
||||
for $index, $value in $list {
|
||||
$s = fmt.printf("%s is %d", $value, $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[a is 0]
|
||||
Vertex: test[b is 1]
|
||||
Vertex: test[c is 2]
|
||||
17
lang/interpret_test/TestAstFunc2/stmtfor07.txtar
Normal file
17
lang/interpret_test/TestAstFunc2/stmtfor07.txtar
Normal file
@@ -0,0 +1,17 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
$index = 42
|
||||
$s = fmt.printf("%s is %d", $value, $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[a is 42]
|
||||
Vertex: test[b is 42]
|
||||
Vertex: test[c is 42]
|
||||
20
lang/interpret_test/TestAstFunc2/stmtfor08.txtar
Normal file
20
lang/interpret_test/TestAstFunc2/stmtfor08.txtar
Normal file
@@ -0,0 +1,20 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
import "math"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
$index = if math.mod($index, 2) == 0 {
|
||||
$index
|
||||
} else {
|
||||
42
|
||||
}
|
||||
$s = fmt.printf("%s is %d", $value, $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
# err: errSetScope: recursive reference while setting scope: not a dag
|
||||
22
lang/interpret_test/TestAstFunc2/stmtfor09.txtar
Normal file
22
lang/interpret_test/TestAstFunc2/stmtfor09.txtar
Normal file
@@ -0,0 +1,22 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
import "math"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
$newindex = if math.mod($index, 2) == 0 {
|
||||
$index
|
||||
} else {
|
||||
42
|
||||
}
|
||||
$s = fmt.printf("%s is %d", $value, $newindex)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[a is 0]
|
||||
Vertex: test[b is 42]
|
||||
Vertex: test[c is 2]
|
||||
21
lang/interpret_test/TestAstFunc2/stmtfor10.txtar
Normal file
21
lang/interpret_test/TestAstFunc2/stmtfor10.txtar
Normal file
@@ -0,0 +1,21 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
$fn = func($x) {
|
||||
"hello " + $x
|
||||
}
|
||||
|
||||
$s = fmt.printf("%s is %d", $fn($value), $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello a is 0]
|
||||
Vertex: test[hello b is 1]
|
||||
Vertex: test[hello c is 2]
|
||||
21
lang/interpret_test/TestAstFunc2/stmtfor11.txtar
Normal file
21
lang/interpret_test/TestAstFunc2/stmtfor11.txtar
Normal file
@@ -0,0 +1,21 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
func fn($x) {
|
||||
"hello " + $x
|
||||
}
|
||||
|
||||
$s = fmt.printf("%s is %d", fn($value), $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello a is 0]
|
||||
Vertex: test[hello b is 1]
|
||||
Vertex: test[hello c is 2]
|
||||
21
lang/interpret_test/TestAstFunc2/stmtfor12.txtar
Normal file
21
lang/interpret_test/TestAstFunc2/stmtfor12.txtar
Normal file
@@ -0,0 +1,21 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
$fn = func($x) {
|
||||
"hello " + $value
|
||||
}
|
||||
|
||||
$s = fmt.printf("%s is %d", $fn("whatever"), $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello a is 0]
|
||||
Vertex: test[hello b is 1]
|
||||
Vertex: test[hello c is 2]
|
||||
21
lang/interpret_test/TestAstFunc2/stmtfor13.txtar
Normal file
21
lang/interpret_test/TestAstFunc2/stmtfor13.txtar
Normal file
@@ -0,0 +1,21 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
func fn($x) {
|
||||
"hello " + $value
|
||||
}
|
||||
|
||||
$s = fmt.printf("%s is %d", fn("whatever"), $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello a is 0]
|
||||
Vertex: test[hello b is 1]
|
||||
Vertex: test[hello c is 2]
|
||||
23
lang/interpret_test/TestAstFunc2/stmtfor14.txtar
Normal file
23
lang/interpret_test/TestAstFunc2/stmtfor14.txtar
Normal file
@@ -0,0 +1,23 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
$fn = func($x) {
|
||||
"hello" + $val
|
||||
}
|
||||
|
||||
$val = " " + $value
|
||||
|
||||
$s = fmt.printf("%s is %d", $fn("whatever"), $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello a is 0]
|
||||
Vertex: test[hello b is 1]
|
||||
Vertex: test[hello c is 2]
|
||||
23
lang/interpret_test/TestAstFunc2/stmtfor15.txtar
Normal file
23
lang/interpret_test/TestAstFunc2/stmtfor15.txtar
Normal file
@@ -0,0 +1,23 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
func fn($x) {
|
||||
"hello" + $val
|
||||
}
|
||||
|
||||
$val = " " + $value
|
||||
|
||||
$s = fmt.printf("%s is %d", fn("whatever"), $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello a is 0]
|
||||
Vertex: test[hello b is 1]
|
||||
Vertex: test[hello c is 2]
|
||||
22
lang/interpret_test/TestAstFunc2/stmtfor16.txtar
Normal file
22
lang/interpret_test/TestAstFunc2/stmtfor16.txtar
Normal file
@@ -0,0 +1,22 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
class foo($x) {
|
||||
$result = "hello " + $x
|
||||
}
|
||||
include foo($value) as included
|
||||
|
||||
$s = fmt.printf("%s is %d", $included.result, $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello a is 0]
|
||||
Vertex: test[hello b is 1]
|
||||
Vertex: test[hello c is 2]
|
||||
22
lang/interpret_test/TestAstFunc2/stmtfor17.txtar
Normal file
22
lang/interpret_test/TestAstFunc2/stmtfor17.txtar
Normal file
@@ -0,0 +1,22 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$word = "hello"
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
class foo($x) {
|
||||
$result = $word + " " + $x
|
||||
}
|
||||
include foo($value) as included
|
||||
|
||||
$s = fmt.printf("%s is %d", $included.result, $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello a is 0]
|
||||
Vertex: test[hello b is 1]
|
||||
Vertex: test[hello c is 2]
|
||||
22
lang/interpret_test/TestAstFunc2/stmtfor18.txtar
Normal file
22
lang/interpret_test/TestAstFunc2/stmtfor18.txtar
Normal file
@@ -0,0 +1,22 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
class foo($x) {
|
||||
$result = "hello " + $value
|
||||
}
|
||||
include foo("whatever") as included
|
||||
|
||||
$s = fmt.printf("%s is %d", $included.result, $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello a is 0]
|
||||
Vertex: test[hello b is 1]
|
||||
Vertex: test[hello c is 2]
|
||||
22
lang/interpret_test/TestAstFunc2/stmtfor19.txtar
Normal file
22
lang/interpret_test/TestAstFunc2/stmtfor19.txtar
Normal file
@@ -0,0 +1,22 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$word = "hello"
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
class foo($x) {
|
||||
$result = $word + " " + $value
|
||||
}
|
||||
include foo("whatever") as included
|
||||
|
||||
$s = fmt.printf("%s is %d", $included.result, $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello a is 0]
|
||||
Vertex: test[hello b is 1]
|
||||
Vertex: test[hello c is 2]
|
||||
24
lang/interpret_test/TestAstFunc2/stmtfor20.txtar
Normal file
24
lang/interpret_test/TestAstFunc2/stmtfor20.txtar
Normal file
@@ -0,0 +1,24 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
class foo($x) {
|
||||
$result = "hello" + $val
|
||||
}
|
||||
include foo("whatever") as included
|
||||
|
||||
$val = " " + $value
|
||||
|
||||
$s = fmt.printf("%s is %d", $included.result, $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello a is 0]
|
||||
Vertex: test[hello b is 1]
|
||||
Vertex: test[hello c is 2]
|
||||
24
lang/interpret_test/TestAstFunc2/stmtfor21.txtar
Normal file
24
lang/interpret_test/TestAstFunc2/stmtfor21.txtar
Normal file
@@ -0,0 +1,24 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$word = "hello"
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
class foo($x) {
|
||||
$result = $word + $val
|
||||
}
|
||||
include foo("whatever") as included
|
||||
|
||||
$val = " " + $value
|
||||
|
||||
$s = fmt.printf("%s is %d", $included.result, $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello a is 0]
|
||||
Vertex: test[hello b is 1]
|
||||
Vertex: test[hello c is 2]
|
||||
24
lang/interpret_test/TestAstFunc2/stmtfor22.txtar
Normal file
24
lang/interpret_test/TestAstFunc2/stmtfor22.txtar
Normal file
@@ -0,0 +1,24 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list1 = ["a", "b", "c",]
|
||||
$list2 = [42, 13, -4,]
|
||||
|
||||
for $index1, $value1 in $list1 {
|
||||
for $index2, $value2 in $list2 {
|
||||
|
||||
$s = fmt.printf("%s is %d", $value1, $value2)
|
||||
test [$s,] {}
|
||||
}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[a is 42]
|
||||
Vertex: test[b is 42]
|
||||
Vertex: test[c is 42]
|
||||
Vertex: test[a is 13]
|
||||
Vertex: test[b is 13]
|
||||
Vertex: test[c is 13]
|
||||
Vertex: test[a is -4]
|
||||
Vertex: test[b is -4]
|
||||
Vertex: test[c is -4]
|
||||
31
lang/interpret_test/TestAstFunc2/stmtfor23.txtar
Normal file
31
lang/interpret_test/TestAstFunc2/stmtfor23.txtar
Normal file
@@ -0,0 +1,31 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list0 = ["a", "b", "c",]
|
||||
$list1 = ["d", "e", "f",]
|
||||
$list2 = ["g", "h", "i",]
|
||||
$list3 = ["j", "k", "l",]
|
||||
|
||||
$list = [$list0, $list1, $list2, $list3,]
|
||||
|
||||
for $index, $value in $list {
|
||||
for $i, $v in $value {
|
||||
|
||||
$s = fmt.printf("%s is %d", $v, $i+$index)
|
||||
test [$s,] {}
|
||||
}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[a is 0]
|
||||
Vertex: test[b is 1]
|
||||
Vertex: test[c is 2]
|
||||
Vertex: test[d is 1]
|
||||
Vertex: test[e is 2]
|
||||
Vertex: test[f is 3]
|
||||
Vertex: test[g is 2]
|
||||
Vertex: test[h is 3]
|
||||
Vertex: test[i is 4]
|
||||
Vertex: test[j is 3]
|
||||
Vertex: test[k is 4]
|
||||
Vertex: test[l is 5]
|
||||
17
lang/interpret_test/TestAstFunc2/stmtfor24.txtar
Normal file
17
lang/interpret_test/TestAstFunc2/stmtfor24.txtar
Normal file
@@ -0,0 +1,17 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a",]
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
$fn = func() {
|
||||
"hello " + $value
|
||||
}
|
||||
|
||||
$s = $fn()
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello a]
|
||||
17
lang/interpret_test/TestAstFunc2/stmtfor25.txtar
Normal file
17
lang/interpret_test/TestAstFunc2/stmtfor25.txtar
Normal file
@@ -0,0 +1,17 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a",]
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
func fn() {
|
||||
"hello " + $value
|
||||
}
|
||||
|
||||
$s = fn()
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello a]
|
||||
41
lang/interpret_test/TestAstFunc2/stmtfor26.txtar
Normal file
41
lang/interpret_test/TestAstFunc2/stmtfor26.txtar
Normal file
@@ -0,0 +1,41 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list1 = ["a", "b", "c",]
|
||||
$list2 = ["x", "y", "z",]
|
||||
|
||||
$word = "hello"
|
||||
|
||||
for $index1, $value1 in $list1 {
|
||||
for $index2, $value2 in $list2 {
|
||||
|
||||
class foo($x, $y) {
|
||||
$result = "hello " + $x + $y + $value1 + $value2
|
||||
$result1 = $x + $value1
|
||||
$result2 = $y + $value2
|
||||
}
|
||||
include foo($value1, $value2) as included
|
||||
|
||||
$s = fmt.printf("%s is {%d,%d}", $included.result, $index1, $index2)
|
||||
$s1 = fmt.printf("one: %s", $included.result1)
|
||||
$s2 = fmt.printf("two: %s", $included.result2)
|
||||
test [$s, $s1, $s2,] {}
|
||||
}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello axax is {0,0}]
|
||||
Vertex: test[hello ayay is {0,1}]
|
||||
Vertex: test[hello azaz is {0,2}]
|
||||
Vertex: test[hello bxbx is {1,0}]
|
||||
Vertex: test[hello byby is {1,1}]
|
||||
Vertex: test[hello bzbz is {1,2}]
|
||||
Vertex: test[hello cxcx is {2,0}]
|
||||
Vertex: test[hello cycy is {2,1}]
|
||||
Vertex: test[hello czcz is {2,2}]
|
||||
Vertex: test[one: aa]
|
||||
Vertex: test[one: bb]
|
||||
Vertex: test[one: cc]
|
||||
Vertex: test[two: xx]
|
||||
Vertex: test[two: yy]
|
||||
Vertex: test[two: zz]
|
||||
38
lang/interpret_test/TestAstFunc2/stmtfor27.txtar
Normal file
38
lang/interpret_test/TestAstFunc2/stmtfor27.txtar
Normal file
@@ -0,0 +1,38 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list1 = ["a", "b", "c",]
|
||||
$list2 = ["x", "y", "z",]
|
||||
|
||||
$word = "hello"
|
||||
|
||||
for $index1, $value1 in $list1 {
|
||||
|
||||
class foo($x, $y) {
|
||||
$result = "hello " + $x + $y + $value1
|
||||
$result1 = $x + $value1
|
||||
}
|
||||
|
||||
for $index2, $value2 in $list2 {
|
||||
|
||||
include foo($value1, $value2) as included
|
||||
|
||||
$s = fmt.printf("%s is {%d,%d}", $included.result, $index1, $index2)
|
||||
$s1 = fmt.printf("one: %s", $included.result1)
|
||||
test [$s, $s1,] {}
|
||||
}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello axa is {0,0}]
|
||||
Vertex: test[hello aya is {0,1}]
|
||||
Vertex: test[hello aza is {0,2}]
|
||||
Vertex: test[hello bxb is {1,0}]
|
||||
Vertex: test[hello byb is {1,1}]
|
||||
Vertex: test[hello bzb is {1,2}]
|
||||
Vertex: test[hello cxc is {2,0}]
|
||||
Vertex: test[hello cyc is {2,1}]
|
||||
Vertex: test[hello czc is {2,2}]
|
||||
Vertex: test[one: aa]
|
||||
Vertex: test[one: bb]
|
||||
Vertex: test[one: cc]
|
||||
19
lang/interpret_test/TestAstFunc2/stmtfor28.txtar
Normal file
19
lang/interpret_test/TestAstFunc2/stmtfor28.txtar
Normal file
@@ -0,0 +1,19 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
for $i, $x in $list {
|
||||
|
||||
func foo($y) {
|
||||
"hello" + $x + $y
|
||||
}
|
||||
|
||||
$s = foo($x)
|
||||
|
||||
test [$s,] {}
|
||||
}
|
||||
-- OUTPUT --
|
||||
Vertex: test[helloaa]
|
||||
Vertex: test[hellobb]
|
||||
Vertex: test[hellocc]
|
||||
22
lang/interpret_test/TestAstFunc2/stmtfor29.txtar
Normal file
22
lang/interpret_test/TestAstFunc2/stmtfor29.txtar
Normal file
@@ -0,0 +1,22 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
class foo() {
|
||||
test [$value + fmt.printf("%d", $index),] {}
|
||||
}
|
||||
include foo() # as included
|
||||
|
||||
#$s = fmt.printf("%s is %d", $included.result, $index)
|
||||
#test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[a0]
|
||||
Vertex: test[b1]
|
||||
Vertex: test[c2]
|
||||
23
lang/interpret_test/TestAstFunc2/stmtfor30.txtar
Normal file
23
lang/interpret_test/TestAstFunc2/stmtfor30.txtar
Normal file
@@ -0,0 +1,23 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
class foo($x) {
|
||||
$result = "hello " + $x
|
||||
test [$result,] {}
|
||||
}
|
||||
include foo($value) # as included
|
||||
|
||||
#$s = fmt.printf("%s is %d", $included.result, $index)
|
||||
#test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello a]
|
||||
Vertex: test[hello b]
|
||||
Vertex: test[hello c]
|
||||
24
lang/interpret_test/TestAstFunc2/stmtfor31.txtar
Normal file
24
lang/interpret_test/TestAstFunc2/stmtfor31.txtar
Normal file
@@ -0,0 +1,24 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
class foo($x) {
|
||||
func result($s) {
|
||||
$s + $x + $value
|
||||
}
|
||||
}
|
||||
include foo($value) as included
|
||||
|
||||
$s = fmt.printf("%s is %d", included.result($value), $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[aaa is 0]
|
||||
Vertex: test[bbb is 1]
|
||||
Vertex: test[ccc is 2]
|
||||
24
lang/interpret_test/TestAstFunc2/stmtfor32.txtar
Normal file
24
lang/interpret_test/TestAstFunc2/stmtfor32.txtar
Normal file
@@ -0,0 +1,24 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
class foo($x) {
|
||||
$result = "hello" + $x + $value
|
||||
}
|
||||
include foo($value) as thing
|
||||
$result = "please"
|
||||
|
||||
# XXX: add $thing.some_func and so on... add more tests says sam.
|
||||
$s = fmt.printf("%s is %d is %s", $thing.result, $index, $result)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[helloaa is 0 is please]
|
||||
Vertex: test[hellobb is 1 is please]
|
||||
Vertex: test[hellocc is 2 is please]
|
||||
24
lang/interpret_test/TestAstFunc2/stmtfor33.txtar
Normal file
24
lang/interpret_test/TestAstFunc2/stmtfor33.txtar
Normal file
@@ -0,0 +1,24 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
class foo($x) {
|
||||
$result = func($s) {
|
||||
$s + $x + $value
|
||||
}
|
||||
}
|
||||
include foo($value) as included
|
||||
|
||||
$s = fmt.printf("%s is %d", $included.result($value), $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[aaa is 0]
|
||||
Vertex: test[bbb is 1]
|
||||
Vertex: test[ccc is 2]
|
||||
23
lang/interpret_test/TestAstFunc2/stmtfor34.txtar
Normal file
23
lang/interpret_test/TestAstFunc2/stmtfor34.txtar
Normal file
@@ -0,0 +1,23 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
class foo() {
|
||||
$result = "hello " + $value + fmt.printf("%d", $index)
|
||||
test [$result,] {}
|
||||
}
|
||||
include foo() # as included
|
||||
|
||||
#$s = fmt.printf("%s is %d", $included.result, $index)
|
||||
#test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello a0]
|
||||
Vertex: test[hello b1]
|
||||
Vertex: test[hello c2]
|
||||
32
lang/interpret_test/TestAstFunc2/stmtfor35.txtar
Normal file
32
lang/interpret_test/TestAstFunc2/stmtfor35.txtar
Normal file
@@ -0,0 +1,32 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list1 = ["a", "b", "c",]
|
||||
$list2 = ["x", "y", "z",]
|
||||
|
||||
$word = "hello"
|
||||
|
||||
class foo($x, $y) {
|
||||
$result = "hello " + $x + $y
|
||||
}
|
||||
|
||||
for $index1, $value1 in $list1 {
|
||||
for $index2, $value2 in $list2 {
|
||||
|
||||
include foo($value1, $value2) as included
|
||||
|
||||
$s = fmt.printf("%s is {%d,%d}", $included.result, $index1, $index2)
|
||||
test [$s,] {}
|
||||
}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello ax is {0,0}]
|
||||
Vertex: test[hello ay is {0,1}]
|
||||
Vertex: test[hello az is {0,2}]
|
||||
Vertex: test[hello bx is {1,0}]
|
||||
Vertex: test[hello by is {1,1}]
|
||||
Vertex: test[hello bz is {1,2}]
|
||||
Vertex: test[hello cx is {2,0}]
|
||||
Vertex: test[hello cy is {2,1}]
|
||||
Vertex: test[hello cz is {2,2}]
|
||||
29
lang/interpret_test/TestAstFunc2/stmtfor36.txtar
Normal file
29
lang/interpret_test/TestAstFunc2/stmtfor36.txtar
Normal file
@@ -0,0 +1,29 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
#$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
class foo($x) {
|
||||
#$result = "hello" + $x + $value # harder
|
||||
#$result = $value # works
|
||||
#$result = $x # works
|
||||
$resultx = "hello" + $x # harder
|
||||
#$result = "hello" + $value # harder
|
||||
#$result = $x + $value # harder
|
||||
}
|
||||
include foo($value)# as included
|
||||
$result = "please"
|
||||
|
||||
# XXX: add $included.some_func and so on... add more tests says sam.
|
||||
$s = fmt.printf("%s is %d is %s", $value, $index, $result)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[a is 0 is please]
|
||||
Vertex: test[b is 1 is please]
|
||||
Vertex: test[c is 2 is please]
|
||||
28
lang/interpret_test/TestAstFunc2/stmtfor37.txtar
Normal file
28
lang/interpret_test/TestAstFunc2/stmtfor37.txtar
Normal file
@@ -0,0 +1,28 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
$index = 42 # should be out of scope
|
||||
|
||||
for $index, $value in $list {
|
||||
|
||||
class foo($x) {
|
||||
$result = func($y1) {
|
||||
"hello" + $x + $value + $y1
|
||||
}
|
||||
}
|
||||
include foo($value) as thing
|
||||
$result = func($y2) {
|
||||
"please" + $y2
|
||||
}
|
||||
|
||||
# XXX: add $thing.some_func and so on... add more tests says sam.
|
||||
$s = fmt.printf("%s is %d is %s", $thing.result("!"), $index, $result("!"))
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[helloaa! is 0 is please!]
|
||||
Vertex: test[hellobb! is 1 is please!]
|
||||
Vertex: test[hellocc! is 2 is please!]
|
||||
21
lang/interpret_test/TestAstFunc2/stmtfor38.txtar
Normal file
21
lang/interpret_test/TestAstFunc2/stmtfor38.txtar
Normal file
@@ -0,0 +1,21 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list1 = ["a", "b", "c",]
|
||||
|
||||
class foo($x) {
|
||||
$result = "hello " + $x
|
||||
}
|
||||
|
||||
for $index, $value in $list1 {
|
||||
|
||||
include foo($value) as included
|
||||
|
||||
$s = fmt.printf("%s is %d", $included.result, $index)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello a is 0]
|
||||
Vertex: test[hello b is 1]
|
||||
Vertex: test[hello c is 2]
|
||||
21
lang/interpret_test/TestAstFunc2/stmtfor39.txtar
Normal file
21
lang/interpret_test/TestAstFunc2/stmtfor39.txtar
Normal file
@@ -0,0 +1,21 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list1 = ["a", "b", "c",]
|
||||
|
||||
class foo($x) {
|
||||
$result = "hello " + fmt.printf("%d", $x)
|
||||
}
|
||||
|
||||
for $index1, $value1 in $list1 {
|
||||
|
||||
include foo($index1) as included
|
||||
|
||||
$s = fmt.printf("%s is %d", $included.result, $index1)
|
||||
test [$s,] {}
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
Vertex: test[hello 0 is 0]
|
||||
Vertex: test[hello 1 is 1]
|
||||
Vertex: test[hello 2 is 2]
|
||||
10
lang/interpret_test/TestAstFunc2/stmtfor40.txtar
Normal file
10
lang/interpret_test/TestAstFunc2/stmtfor40.txtar
Normal file
@@ -0,0 +1,10 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
for $index, $value in $list {
|
||||
$foo = $index # does nothing
|
||||
}
|
||||
|
||||
-- OUTPUT --
|
||||
24
lang/interpret_test/TestAstFunc2/stmtfor41.txtar
Normal file
24
lang/interpret_test/TestAstFunc2/stmtfor41.txtar
Normal file
@@ -0,0 +1,24 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
import "thing.mcl" # works
|
||||
|
||||
for $index, $value in $list {
|
||||
# The semantics are that only one copy of an import is needed... Not one per iteration.
|
||||
# XXX: Error: could not find `inside` in env for ExprIterated
|
||||
# XXX: I added a hack to catch this obvious case
|
||||
#import "thing.mcl" # XXX: doesn't work :(
|
||||
|
||||
$x = 42 + $thing.inside
|
||||
|
||||
$s = fmt.printf("%s is %d = %d", $value, $index, $x)
|
||||
test [$s,] {}
|
||||
}
|
||||
-- thing.mcl --
|
||||
$inside = 13
|
||||
-- OUTPUT --
|
||||
Vertex: test[a is 0 = 55]
|
||||
Vertex: test[b is 1 = 55]
|
||||
Vertex: test[c is 2 = 55]
|
||||
27
lang/interpret_test/TestAstFunc2/stmtfor42.txtar
Normal file
27
lang/interpret_test/TestAstFunc2/stmtfor42.txtar
Normal file
@@ -0,0 +1,27 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
import "thing.mcl" # works
|
||||
|
||||
for $index, $value in $list {
|
||||
# The semantics are that only one copy of an import is needed... Not one per iteration.
|
||||
# XXX: Error: could not find `inside` in env for ExprIterated
|
||||
class foo($y) {
|
||||
#import "thing.mcl" # XXX: doesn't work :(
|
||||
$out = $y + 7 + $thing.inside
|
||||
}
|
||||
include foo($index) as usefoo
|
||||
|
||||
$x = 42 + $usefoo.out
|
||||
|
||||
$s = fmt.printf("%s is %d = %d", $value, $index, $x)
|
||||
test [$s,] {}
|
||||
}
|
||||
-- thing.mcl --
|
||||
$inside = 13
|
||||
-- OUTPUT --
|
||||
Vertex: test[a is 0 = 62]
|
||||
Vertex: test[b is 1 = 63]
|
||||
Vertex: test[c is 2 = 64]
|
||||
22
lang/interpret_test/TestAstFunc2/stmtfor43.txtar
Normal file
22
lang/interpret_test/TestAstFunc2/stmtfor43.txtar
Normal file
@@ -0,0 +1,22 @@
|
||||
-- main.mcl --
|
||||
import "fmt"
|
||||
|
||||
$list = ["a", "b", "c",]
|
||||
|
||||
#import "thing.mcl" # works
|
||||
|
||||
for $index, $value in $list {
|
||||
# The semantics are that only one copy of an import is needed... Not one per iteration.
|
||||
# XXX: Error: could not find `inside` in env for ExprIterated
|
||||
# XXX: We don't want this to be an error, but it is for now.
|
||||
import "thing.mcl" # XXX: doesn't work :(
|
||||
|
||||
$x = 42 + $thing.inside
|
||||
|
||||
$s = fmt.printf("%s is %d = %d", $value, $index, $x)
|
||||
test [$s,] {}
|
||||
}
|
||||
-- thing.mcl --
|
||||
$inside = 13
|
||||
-- OUTPUT --
|
||||
# err: errInit: a StmtImport can't be contained inside a StmtFor
|
||||
@@ -10,4 +10,4 @@ test "test2" {
|
||||
anotherstr => $id("hello"),
|
||||
}
|
||||
-- OUTPUT --
|
||||
# err: errUnify: unify error with: topLevel(singleton(func(x) { var(x) })): type error: int != str
|
||||
# err: errUnify: unify error with: topLevel(singleton(func(x) { var(x) })): type error: str != int
|
||||
|
||||
@@ -12,4 +12,4 @@ class use_polymorphically($id) {
|
||||
}
|
||||
include use_polymorphically(func($x) {$x})
|
||||
-- OUTPUT --
|
||||
# err: errUnify: unify error with: topLevel(singleton(func(x) { var(x) })): type error: int != str
|
||||
# err: errUnify: unify error with: topLevel(singleton(func(x) { var(x) })): type error: str != int
|
||||
|
||||
11
lang/interpret_test/TestAstFunc2/wat.txtar
Normal file
11
lang/interpret_test/TestAstFunc2/wat.txtar
Normal file
@@ -0,0 +1,11 @@
|
||||
-- main.mcl --
|
||||
$x = ["foo", "bar",]
|
||||
$f = func() {
|
||||
$x[0]
|
||||
}
|
||||
$y = $f()
|
||||
$z = $f()
|
||||
|
||||
test ["${y}${z}",] {}
|
||||
-- OUTPUT --
|
||||
Vertex: test[foofoo]
|
||||
14
lang/interpret_test/TestAstFunc2/watsam1.txtar
Normal file
14
lang/interpret_test/TestAstFunc2/watsam1.txtar
Normal file
@@ -0,0 +1,14 @@
|
||||
-- main.mcl --
|
||||
$call = func($f, $arg) {
|
||||
$f($arg)
|
||||
}
|
||||
|
||||
$lambda = func($x) {
|
||||
$call(func($z) { "hello" + $x }, "nope")
|
||||
}
|
||||
|
||||
$s = $lambda("world")
|
||||
|
||||
test [$s,] {}
|
||||
-- OUTPUT --
|
||||
Vertex: test[helloworld]
|
||||
21
lang/interpret_test/TestAstFunc2/watsam2.txtar
Normal file
21
lang/interpret_test/TestAstFunc2/watsam2.txtar
Normal file
@@ -0,0 +1,21 @@
|
||||
-- main.mcl --
|
||||
$call = func($f, $arg) {
|
||||
$f($arg)
|
||||
}
|
||||
|
||||
$lambda = func($x) {
|
||||
$call(
|
||||
if $x == "nope1" {
|
||||
func($z) { "nope2" + $x }
|
||||
} else {
|
||||
func($z) { "hello" + $x }
|
||||
},
|
||||
"bye"
|
||||
)
|
||||
}
|
||||
|
||||
$s = $lambda("world")
|
||||
|
||||
test [$s,] {}
|
||||
-- OUTPUT --
|
||||
Vertex: test[helloworld]
|
||||
27
lang/lang.go
27
lang/lang.go
@@ -290,16 +290,23 @@ func (obj *Lang) Init(ctx context.Context) error {
|
||||
// we assume that for some given code, the list of funcs doesn't change
|
||||
// iow, we don't support variable, variables or absurd things like that
|
||||
obj.graph = &pgraph.Graph{Name: "functionGraph"}
|
||||
env := make(map[string]interfaces.Func)
|
||||
for k, v := range scope.Variables {
|
||||
g, builtinFunc, err := v.Graph(nil)
|
||||
if err != nil {
|
||||
return errwrap.Wrapf(err, "calling Graph on builtins")
|
||||
}
|
||||
obj.graph.AddGraph(g)
|
||||
env[k] = builtinFunc
|
||||
}
|
||||
g, err := obj.ast.Graph() // build the graph of functions
|
||||
env := interfaces.EmptyEnv()
|
||||
// XXX: Do we need to do something like this?
|
||||
//for k, v := range scope.Variables {
|
||||
// g, builtinFunc, err := v.Graph(nil)
|
||||
// if err != nil {
|
||||
// return errwrap.Wrapf(err, "calling Graph on builtins")
|
||||
// }
|
||||
// obj.graph.AddGraph(g)
|
||||
// env.Variables[k] = builtinFunc // XXX: Ask Sam (.Functions ???)
|
||||
//}
|
||||
//for k, v := range scope.Functions {
|
||||
// env.Functions[k] = &interfaces.Closure{
|
||||
// Env: interfaces.EmptyEnv(),
|
||||
// Expr: v,
|
||||
// }
|
||||
//}
|
||||
g, err := obj.ast.Graph(env) // build the graph of functions
|
||||
if err != nil {
|
||||
return errwrap.Wrapf(err, "could not generate function graph")
|
||||
}
|
||||
|
||||
@@ -154,6 +154,11 @@
|
||||
lval.str = yylex.Text()
|
||||
return IN
|
||||
}
|
||||
/for/ {
|
||||
yylex.pos(lval) // our pos
|
||||
lval.str = yylex.Text()
|
||||
return FOR
|
||||
}
|
||||
/\->/ {
|
||||
yylex.pos(lval) // our pos
|
||||
lval.str = yylex.Text()
|
||||
|
||||
@@ -92,7 +92,7 @@ func init() {
|
||||
%token OPEN_CURLY CLOSE_CURLY
|
||||
%token OPEN_PAREN CLOSE_PAREN
|
||||
%token OPEN_BRACK CLOSE_BRACK
|
||||
%token IF ELSE
|
||||
%token IF ELSE FOR
|
||||
%token BOOL STRING INTEGER FLOAT
|
||||
%token EQUALS DOLLAR
|
||||
%token COMMA COLON SEMICOLON
|
||||
@@ -216,6 +216,18 @@ stmt:
|
||||
}
|
||||
locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt)
|
||||
}
|
||||
// iterate over lists
|
||||
// `for $index, $value in $list { <body> }`
|
||||
| FOR var_identifier COMMA var_identifier IN expr OPEN_CURLY prog CLOSE_CURLY
|
||||
{
|
||||
$$.stmt = &ast.StmtFor{
|
||||
Index: $2.str, // no $ prefix
|
||||
Value: $4.str, // no $ prefix
|
||||
Expr: $6.expr, // XXX: name this List ?
|
||||
Body: $8.stmt,
|
||||
}
|
||||
locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt)
|
||||
}
|
||||
// this is the named version, iow, a user-defined function (statement)
|
||||
// `func name() { <expr> }`
|
||||
// `func name(<arg>) { <expr> }`
|
||||
|
||||
Reference in New Issue
Block a user