package eval
import (
)
type compileBuiltin func(*compiler, *parse.Form) effectOp
var builtinSpecials map[string]compileBuiltin
var IsBuiltinSpecial = map[string]bool{}
type noSuchModule struct{ spec string }
func ( noSuchModule) () string { return "no such module: " + .spec }
func () {
builtinSpecials = map[string]compileBuiltin{
"var": compileVar,
"set": compileSet,
"del": compileDel,
"fn": compileFn,
"use": compileUse,
"and": compileAnd,
"or": compileOr,
"if": compileIf,
"while": compileWhile,
"for": compileFor,
"try": compileTry,
}
for := range builtinSpecials {
IsBuiltinSpecial[] = true
}
}
func ( *compiler, *parse.Form) effectOp {
:= lvaluesGroup{rest: -1}
for , := range .Args {
if parse.SourceText() == "=" {
var valuesOp
if == len(.Args)-1 {
= nopValuesOp{diag.PointRanging(.Range().To)}
} else {
= seqValuesOp{
diag.MixedRanging(.Args[+1], .Args[len(.Args)-1]),
.compoundOps(.Args[+1:])}
}
return &assignOp{.Range(), , }
}
if len(.Indexings) != 1 {
.errorpf(, "variable name must be a single string literal")
}
if len(.Indexings[0].Indicies) > 0 {
.errorpf(, "variable name must not have indicies")
}
:= .Indexings[0].Head
if !parse.ValidLHSVariable(, true) {
.errorpf(, "invalid variable name")
}
:= .Value
if !IsUnqualified() {
.errorpf(, "variable declared in var must be unqualified")
}
, := SplitSigil()
if == "@" {
if .rest != -1 {
.errorpf(, "multiple variable names with @ not allowed")
}
.rest =
}
:= .thisScope().add()
.lvalues = append(.lvalues,
lvalue{.Range(), &varRef{localScope, , nil}, nil, nil})
}
return nopOp{}
}
func ( string) bool {
:= strings.IndexByte(, ':')
return == -1 || == len()-1
}
func ( *compiler, *parse.Form) effectOp {
:= -1
for , := range .Args {
if parse.SourceText() == "=" {
=
break
}
}
if == -1 {
.errorpf(diag.PointRanging(.Range().To), "need = and right-hand-side")
}
:= .parseCompoundLValues(.Args[:])
var valuesOp
if == len(.Args)-1 {
= nopValuesOp{diag.PointRanging(.Range().To)}
} else {
= seqValuesOp{
diag.MixedRanging(.Args[+1], .Args[len(.Args)-1]),
.compoundOps(.Args[+1:])}
}
return &assignOp{.Range(), , }
}
const delArgMsg = "arguments to del must be variable or variable elements"
func ( *compiler, *parse.Form) effectOp {
var []effectOp
for , := range .Args {
if len(.Indexings) != 1 {
.errorpf(, delArgMsg)
continue
}
, := .Indexings[0].Head, .Indexings[0].Indicies
if .Type == parse.Variable {
.errorpf(, "arguments to del must drop $")
} else if !parse.ValidLHSVariable(, false) {
.errorpf(, delArgMsg)
}
:= .Value
var effectOp
:= resolveVarRef(, , nil)
if == nil {
.errorpf(, "no variable $%s", .Value)
continue
}
if len() == 0 {
if .scope == envScope {
= delEnvVarOp{.Range(), .subNames[0]}
} else if .scope == localScope && len(.subNames) == 0 {
= delLocalVarOp{.index}
.thisScope().deleted[.index] = true
} else {
.errorpf(, "only variables in local: or E: can be deleted")
continue
}
} else {
= newDelElementOp(, .Range().From, .Range().To, .arrayOps())
}
= append(, )
}
return seqOp{}
}
type delLocalVarOp struct{ index int }
func ( delLocalVarOp) ( *Frame) Exception {
.local.slots[.index] = nil
return nil
}
type delEnvVarOp struct {
diag.Ranging
name string
}
func ( delEnvVarOp) ( *Frame) Exception {
return .errorp(, os.Unsetenv(.name))
}
func ( *varRef, , int, []valuesOp) effectOp {
:= make([]int, len()+1)
[0] =
for , := range {
[+1] = .Range().To
}
return &delElemOp{, , , }
}
type delElemOp struct {
ref *varRef
indexOps []valuesOp
begin int
ends []int
}
func ( *delElemOp) () diag.Ranging {
return diag.Ranging{From: .begin, To: .ends[0]}
}
func ( *delElemOp) ( *Frame) Exception {
var []interface{}
for , := range .indexOps {
, := .exec()
if != nil {
return
}
if len() != 1 {
return .errorpf(, "index must evaluate to a single value in argument to del")
}
= append(, [0])
}
:= vars.DelElement(deref(, .ref), )
if != nil {
if := vars.ElementErrorLevel(); >= 0 {
return .errorp(diag.Ranging{From: .begin, To: .ends[]}, )
}
return .errorp(, )
}
return nil
}
func ( *compiler, *parse.Form) effectOp {
:= .walkArgs()
:= .next()
:= stringLiteralOrError(, , "function name")
:= .nextMustLambda("function body")
.mustEnd()
:= .thisScope().add( + FnSuffix)
:= .lambda()
return fnOp{.Range(), , }
}
type fnOp struct {
keywordRange diag.Ranging
varIndex int
lambdaOp valuesOp
}
func ( fnOp) ( *Frame) Exception {
.local.slots[.varIndex].Set(NewGoFn("<shouldn't be called>", nop))
, := .lambdaOp.exec()
if != nil {
return
}
:= [0].(*closure)
.Op = fnWrap{.Op}
return .errorp(.keywordRange, .local.slots[.varIndex].Set())
}
type fnWrap struct{ effectOp }
func ( fnWrap) () diag.Ranging { return .effectOp.(diag.Ranger).Range() }
func ( fnWrap) ( *Frame) Exception {
:= .effectOp.exec()
if != nil && .Reason() != Return {
return
}
return nil
}
func ( *compiler, *parse.Form) effectOp {
var , string
switch len(.Args) {
case 0:
:= .Head.Range().To
.errorpf(diag.PointRanging(), "lack module name")
case 1:
= stringLiteralOrError(, .Args[0], "module spec")
= [strings.LastIndexByte(, '/')+1:]
case 2:
= stringLiteralOrError(, .Args[0], "module spec")
= stringLiteralOrError(, .Args[1], "module name")
default:
.errorpf(diag.MixedRanging(.Args[2], .Args[len(.Args)-1]),
"superfluous argument(s)")
}
return useOp{.Range(), .thisScope().add( + NsSuffix), }
}
type useOp struct {
diag.Ranging
varIndex int
spec string
}
func ( useOp) ( *Frame) Exception {
, := use(, .spec, )
if != nil {
return .errorp(, )
}
.local.slots[.varIndex].Set()
return nil
}
var bundledModules = bundled.Get()
func ( *Frame, string, diag.Ranger) (*Ns, error) {
if strings.HasPrefix(, "./") || strings.HasPrefix(, "../") {
var string
if .srcMeta.IsFile {
= filepath.Dir(.srcMeta.Name)
} else {
var error
, = os.Getwd()
if != nil {
return nil,
}
}
:= filepath.Clean( + "/" + + ".elv")
return useFromFile(, , , )
}
if , := .Evaler.modules[]; {
return , nil
}
if , := bundledModules[]; {
return evalModule(, ,
parse.Source{Name: "[bundled " + + "]", Code: }, )
}
:= .Evaler.getLibDir()
if == "" {
return nil, noSuchModule{}
}
return useFromFile(, , +"/"++".elv", )
}
func ( *Frame, , string, diag.Ranger) (*Ns, error) {
if , := .Evaler.modules[]; {
return , nil
}
, := readFileUTF8()
if != nil {
if os.IsNotExist() {
return nil, noSuchModule{}
}
return nil,
}
return evalModule(, , parse.Source{Name: , Code: , IsFile: true}, )
}
func ( *Frame, string, parse.Source, diag.Ranger) (*Ns, error) {
, , := .PrepareEval(, , new(Ns))
if != nil {
return nil,
}
.Evaler.modules[] =
= ()
if != nil {
delete(.Evaler.modules, )
return nil,
}
return , nil
}
func ( *compiler, *parse.Form) effectOp {
return &andOrOp{.compoundOps(.Args), true, false}
}
func ( *compiler, *parse.Form) effectOp {
return &andOrOp{.compoundOps(.Args), false, true}
}
type andOrOp struct {
argOps []valuesOp
init bool
stopAt bool
}
func ( *andOrOp) ( *Frame) Exception {
var interface{} = vals.Bool(.init)
for , := range .argOps {
, := .exec()
if != nil {
return
}
for , := range {
if vals.Bool() == .stopAt {
.OutputChan() <-
return nil
}
=
}
}
.OutputChan() <-
return nil
}
func ( *compiler, *parse.Form) effectOp {
:= .walkArgs()
var []*parse.Compound
var []*parse.Primary
:= "if"
for {
= append(, .next())
= append(, .nextMustLambda())
if !.nextIs("elif") {
break
}
= "elif"
}
:= .nextMustLambdaIfAfter("else")
.mustEnd()
:= .compoundOps()
:= .primaryOps()
var valuesOp
if != nil {
= .primaryOp()
}
return &ifOp{.Range(), , , }
}
type ifOp struct {
diag.Ranging
condOps []valuesOp
bodyOps []valuesOp
elseOp valuesOp
}
func ( *ifOp) ( *Frame) Exception {
:= make([]Callable, len(.bodyOps))
for , := range .bodyOps {
[] = execLambdaOp(, )
}
:= execLambdaOp(, .elseOp)
for , := range .condOps {
, := .exec(.fork("if cond"))
if != nil {
return
}
if allTrue() {
return .errorp(, [].Call(.fork("if body"), NoArgs, NoOpts))
}
}
if .elseOp != nil {
return .errorp(, .Call(.fork("if else"), NoArgs, NoOpts))
}
return nil
}
func ( *compiler, *parse.Form) effectOp {
:= .walkArgs()
:= .next()
:= .nextMustLambda("while body")
:= .nextMustLambdaIfAfter("else")
.mustEnd()
:= .compoundOp()
:= .primaryOp()
var valuesOp
if != nil {
= .primaryOp()
}
return &whileOp{.Range(), , , }
}
type whileOp struct {
diag.Ranging
condOp, bodyOp, elseOp valuesOp
}
func ( *whileOp) ( *Frame) Exception {
:= execLambdaOp(, .bodyOp)
:= execLambdaOp(, .elseOp)
:= false
for {
, := .condOp.exec(.fork("while cond"))
if != nil {
return
}
if !allTrue() {
break
}
= true
:= .Call(.fork("while"), NoArgs, NoOpts)
if != nil {
:= .(Exception)
if .Reason() == Continue {
} else if .Reason() == Break {
break
} else {
return
}
}
}
if .elseOp != nil && ! {
return .errorp(, .Call(.fork("while else"), NoArgs, NoOpts))
}
return nil
}
func ( *compiler, *parse.Form) effectOp {
:= .walkArgs()
:= .next()
:= .next()
:= .nextMustLambda("for body")
:= .nextMustLambdaIfAfter("else")
.mustEnd()
:= .compileOneLValue()
:= .compoundOp()
:= .primaryOp()
var valuesOp
if != nil {
= .primaryOp()
}
return &forOp{.Range(), , , , }
}
type forOp struct {
diag.Ranging
lvalue lvalue
iterOp valuesOp
bodyOp valuesOp
elseOp valuesOp
}
func ( *forOp) ( *Frame) Exception {
, := derefLValue(, .lvalue)
if != nil {
return .errorp(, )
}
, := evalForValue(, .iterOp, "value being iterated")
if != nil {
return .errorp(, )
}
:= execLambdaOp(, .bodyOp)
:= execLambdaOp(, .elseOp)
:= false
var error
:= vals.Iterate(, func( interface{}) bool {
= true
:= .Set()
if != nil {
=
return false
}
= .Call(.fork("for"), NoArgs, NoOpts)
if != nil {
:= .(Exception)
if .Reason() == Continue {
} else if .Reason() == Break {
return false
} else {
=
return false
}
}
return true
})
if != nil {
return .errorp(, )
}
if != nil {
return .errorp(, )
}
if ! && != nil {
return .errorp(, .Call(.fork("for else"), NoArgs, NoOpts))
}
return nil
}
func ( *compiler, *parse.Form) effectOp {
logger.Println("compiling try")
:= .walkArgs()
:= .nextMustLambda("try body")
logger.Printf("body is %q", parse.SourceText())
var *parse.Compound
var *parse.Primary
if .nextIs("except") {
logger.Println("except-ing")
:= .peek()
if , := cmpd.StringLiteral(); {
=
.next()
}
= .nextMustLambda("except body")
}
:= .nextMustLambdaIfAfter("else")
:= .nextMustLambdaIfAfter("finally")
.mustEnd()
var lvalue
var , , , valuesOp
= .primaryOp()
if != nil {
= .compileOneLValue()
}
if != nil {
= .primaryOp()
}
if != nil {
= .primaryOp()
}
if != nil {
= .primaryOp()
}
return &tryOp{.Range(), , , , , }
}
type tryOp struct {
diag.Ranging
bodyOp valuesOp
exceptVar lvalue
exceptOp valuesOp
elseOp valuesOp
finallyOp valuesOp
}
func ( *tryOp) ( *Frame) Exception {
:= execLambdaOp(, .bodyOp)
var vars.Var
if .exceptVar.ref != nil {
var error
, = derefLValue(, .exceptVar)
if != nil {
return .errorp(, )
}
}
:= execLambdaOp(, .exceptOp)
:= execLambdaOp(, .elseOp)
:= execLambdaOp(, .finallyOp)
:= .Call(.fork("try body"), NoArgs, NoOpts)
if != nil {
if != nil {
if != nil {
:= .Set(.(Exception))
if != nil {
return .errorp(.exceptVar, )
}
}
= .Call(.fork("try except"), NoArgs, NoOpts)
}
} else {
if != nil {
= .Call(.fork("try else"), NoArgs, NoOpts)
}
}
if != nil {
:= .Call(.fork("try finally"), NoArgs, NoOpts)
if != nil {
return .errorp(, )
}
}
return .errorp(, )
}
func ( *compiler) ( *parse.Compound) lvalue {
if len(.Indexings) != 1 {
.errorpf(, "must be valid lvalue")
}
:= .parseIndexingLValue(.Indexings[0])
if .rest != -1 {
.errorpf(.lvalues[.rest], "rest variable not allowed")
}
if len(.lvalues) != 1 {
.errorpf(, "must be exactly one lvalue")
}
return .lvalues[0]
}
func ( *Frame, valuesOp) Callable {
if == nil {
return nil
}
, := .exec()
if != nil {
panic("must not be erroneous")
}
return [0].(Callable)
}