blob: e1a523a6bd24a620119993f49ce2f66b28d6d954 [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
Herbert Xue3fe7b7f2024-03-15 16:03:22 +080017import "strings"
Colin Cross3f40fa42015-01-30 17:27:36 -080018
19type Scope interface {
20 Get(name string) string
21 Set(name, value string)
Jaewoong Jungacb529b2019-01-07 15:29:01 -080022 Call(name string, args []string) []string
23 SetFunc(name string, f func([]string) []string)
Colin Cross3f40fa42015-01-30 17:27:36 -080024}
25
26type scope struct {
27 variables map[string]string
Jaewoong Jungacb529b2019-01-07 15:29:01 -080028 functions map[string]func([]string) []string
Colin Cross3f40fa42015-01-30 17:27:36 -080029 parent Scope
30}
31
32func (s *scope) Get(name string) string {
33 if val, ok := s.variables[name]; ok {
34 return val
35 } else if s.parent != nil {
36 return s.parent.Get(name)
37 } else if val, ok := builtinScope[name]; ok {
38 return val
39 } else {
40 return "<'" + name + "' unset>"
41 }
42}
43
44func (s *scope) Set(name, value string) {
45 s.variables[name] = value
46}
47
Jaewoong Jungacb529b2019-01-07 15:29:01 -080048func (s *scope) Call(name string, args []string) []string {
Colin Cross3f40fa42015-01-30 17:27:36 -080049 if f, ok := s.functions[name]; ok {
50 return f(args)
51 }
52
Jaewoong Jungacb529b2019-01-07 15:29:01 -080053 return []string{"<func:'" + name + "' unset>"}
Colin Cross3f40fa42015-01-30 17:27:36 -080054}
55
Jaewoong Jungacb529b2019-01-07 15:29:01 -080056func (s *scope) SetFunc(name string, f func([]string) []string) {
Colin Cross3f40fa42015-01-30 17:27:36 -080057 s.functions[name] = f
58}
59
60func NewScope(parent Scope) Scope {
61 return &scope{
62 variables: make(map[string]string),
Jaewoong Jungacb529b2019-01-07 15:29:01 -080063 functions: make(map[string]func([]string) []string),
Colin Cross3f40fa42015-01-30 17:27:36 -080064 parent: parent,
65 }
66}
67
68var builtinScope map[string]string
69
70func init() {
71 builtinScope := make(map[string]string)
Dan Willemsen43398532018-02-21 02:10:29 -080072 builtinScope[builtinDollar] = "$"
Colin Cross3f40fa42015-01-30 17:27:36 -080073}
74
Jaewoong Jungacb529b2019-01-07 15:29:01 -080075func (v Variable) EvalFunction(scope Scope) ([]string, bool) {
Colin Cross3f40fa42015-01-30 17:27:36 -080076 f := v.Name.SplitN(" \t", 2)
77 if len(f) > 1 && f[0].Const() {
78 fname := f[0].Value(nil)
79 if isFunctionName(fname) {
80 args := f[1].Split(",")
81 argVals := make([]string, len(args))
82 for i, a := range args {
83 argVals[i] = a.Value(scope)
84 }
85
86 if fname == "call" {
Colin Cross16daa922015-04-03 16:51:45 -070087 return scope.Call(argVals[0], argVals[1:]), true
Colin Cross3f40fa42015-01-30 17:27:36 -080088 } else {
Herbert Xue3fe7b7f2024-03-15 16:03:22 +080089 return []string{"UNSUPPORTED FUNCTION:" + fname + " " + strings.Join(argVals, " ")}, true
Colin Cross3f40fa42015-01-30 17:27:36 -080090 }
91 }
92 }
93
Jaewoong Jungacb529b2019-01-07 15:29:01 -080094 return []string{""}, false
Colin Cross16daa922015-04-03 16:51:45 -070095}
96
97func (v Variable) Value(scope Scope) string {
98 if ret, ok := v.EvalFunction(scope); ok {
Jaewoong Jungacb529b2019-01-07 15:29:01 -080099 if len(ret) > 1 {
100 panic("Expected a single value, but instead got a list")
101 }
102 return ret[0]
Colin Cross16daa922015-04-03 16:51:45 -0700103 }
Jeff Gastonf3ccfa92017-08-23 17:03:42 -0700104 if scope == nil {
105 panic("Cannot take the value of a variable in a nil scope")
106 }
Colin Cross3f40fa42015-01-30 17:27:36 -0800107 return scope.Get(v.Name.Value(scope))
108}
109
110func toVariable(ms *MakeString) (Variable, bool) {
111 if len(ms.Variables) == 1 && ms.Strings[0] == "" && ms.Strings[1] == "" {
112 return ms.Variables[0], true
113 }
114 return Variable{}, false
115}