blob: 8111c89e1e49807146b8b678934ef8de0afbf567 [file] [log] [blame]
Colin Crossd00350c2017-11-17 10:55:38 -08001// Copyright 2017 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Colin Cross3f40fa42015-01-30 17:27:36 -080015package parser
16
Jeff Gastonf3ccfa92017-08-23 17:03:42 -070017import (
18 "strings"
19)
Colin Cross3f40fa42015-01-30 17:27:36 -080020
21type Scope interface {
22 Get(name string) string
23 Set(name, value string)
Jaewoong Jungacb529b2019-01-07 15:29:01 -080024 Call(name string, args []string) []string
25 SetFunc(name string, f func([]string) []string)
Colin Cross3f40fa42015-01-30 17:27:36 -080026}
27
28type scope struct {
29 variables map[string]string
Jaewoong Jungacb529b2019-01-07 15:29:01 -080030 functions map[string]func([]string) []string
Colin Cross3f40fa42015-01-30 17:27:36 -080031 parent Scope
32}
33
34func (s *scope) Get(name string) string {
35 if val, ok := s.variables[name]; ok {
36 return val
37 } else if s.parent != nil {
38 return s.parent.Get(name)
39 } else if val, ok := builtinScope[name]; ok {
40 return val
41 } else {
42 return "<'" + name + "' unset>"
43 }
44}
45
46func (s *scope) Set(name, value string) {
47 s.variables[name] = value
48}
49
Jaewoong Jungacb529b2019-01-07 15:29:01 -080050func (s *scope) Call(name string, args []string) []string {
Colin Cross3f40fa42015-01-30 17:27:36 -080051 if f, ok := s.functions[name]; ok {
52 return f(args)
53 }
54
Jaewoong Jungacb529b2019-01-07 15:29:01 -080055 return []string{"<func:'" + name + "' unset>"}
Colin Cross3f40fa42015-01-30 17:27:36 -080056}
57
Jaewoong Jungacb529b2019-01-07 15:29:01 -080058func (s *scope) SetFunc(name string, f func([]string) []string) {
Colin Cross3f40fa42015-01-30 17:27:36 -080059 s.functions[name] = f
60}
61
62func NewScope(parent Scope) Scope {
63 return &scope{
64 variables: make(map[string]string),
Jaewoong Jungacb529b2019-01-07 15:29:01 -080065 functions: make(map[string]func([]string) []string),
Colin Cross3f40fa42015-01-30 17:27:36 -080066 parent: parent,
67 }
68}
69
70var builtinScope map[string]string
71
72func init() {
73 builtinScope := make(map[string]string)
Dan Willemsen43398532018-02-21 02:10:29 -080074 builtinScope[builtinDollar] = "$"
Colin Cross3f40fa42015-01-30 17:27:36 -080075}
76
Jaewoong Jungacb529b2019-01-07 15:29:01 -080077func (v Variable) EvalFunction(scope Scope) ([]string, bool) {
Colin Cross3f40fa42015-01-30 17:27:36 -080078 f := v.Name.SplitN(" \t", 2)
79 if len(f) > 1 && f[0].Const() {
80 fname := f[0].Value(nil)
81 if isFunctionName(fname) {
82 args := f[1].Split(",")
83 argVals := make([]string, len(args))
84 for i, a := range args {
85 argVals[i] = a.Value(scope)
86 }
87
88 if fname == "call" {
Colin Cross16daa922015-04-03 16:51:45 -070089 return scope.Call(argVals[0], argVals[1:]), true
Colin Cross3f40fa42015-01-30 17:27:36 -080090 } else {
Jaewoong Jungacb529b2019-01-07 15:29:01 -080091 return []string{"__builtin_func:" + fname + " " + strings.Join(argVals, " ")}, true
Colin Cross3f40fa42015-01-30 17:27:36 -080092 }
93 }
94 }
95
Jaewoong Jungacb529b2019-01-07 15:29:01 -080096 return []string{""}, false
Colin Cross16daa922015-04-03 16:51:45 -070097}
98
99func (v Variable) Value(scope Scope) string {
100 if ret, ok := v.EvalFunction(scope); ok {
Jaewoong Jungacb529b2019-01-07 15:29:01 -0800101 if len(ret) > 1 {
102 panic("Expected a single value, but instead got a list")
103 }
104 return ret[0]
Colin Cross16daa922015-04-03 16:51:45 -0700105 }
Jeff Gastonf3ccfa92017-08-23 17:03:42 -0700106 if scope == nil {
107 panic("Cannot take the value of a variable in a nil scope")
108 }
Colin Cross3f40fa42015-01-30 17:27:36 -0800109 return scope.Get(v.Name.Value(scope))
110}
111
112func toVariable(ms *MakeString) (Variable, bool) {
113 if len(ms.Variables) == 1 && ms.Strings[0] == "" && ms.Strings[1] == "" {
114 return ms.Variables[0], true
115 }
116 return Variable{}, false
117}