func (p *Parser) Parse() *tp.ScriptObject { script := new(tp.ScriptObject) // script.Name = proto.String(p.FullPath) if !p.RootFile || TritiumParserShowRewriterFileName { script.Name = proto.String(filepath.Join(p.ScriptPath, p.FileName)) } else { script.Name = proto.String("__rewriter__") } stmts := tp.ListInstructions() defs := make([]*tp.Function, 0) // Add a new constructor in instruction.go // Look for the namespace directive first. if p.peek().Lexeme == NAMESPACE { p.namespaces() } for p.peek().Lexeme != EOF { switch p.peek().Lexeme { case FUNC: defs = append(defs, p.definition()) case OPEN: p.open(false) default: stmt := p.statement() stmts = append(stmts, stmt) // need to intersperse imports with definitions if constants.Instruction_InstructionType_name[int32(stmt.GetType())] == "IMPORT" { // Make a special function stub that represents the import. // Need to do this because we can't mix definitions and instructions in // the same array. imp := new(tp.Function) imp.Name = proto.String("@import") imp.Description = proto.String(stmt.GetValue()) defs = append(defs, imp) } } } if len(defs) == 0 { defs = nil } var line int32 if len(stmts) == 0 { stmts = nil } else { line = *stmts[0].LineNumber } script.Functions = defs script.Root = tp.MakeBlock(stmts, line) // if defs == nil && p.currentNamespace() != "tritium" { // panic(fmt.Sprintf("%s: %d -- custom modules may only be declared in function definition files", p.FileName, moduleLineNum)) // } return script }
func (p *Parser) block() (stmts []*tp.Instruction) { stmts = tp.ListInstructions() p.pop() // pop the lbrace // check for a localized namespace declaration at the top of the block pushedNamespace := false if p.peek().Lexeme == NAMESPACE { p.namespaces() pushedNamespace = true } for p.peek().Lexeme != RBRACE { if p.peek().Lexeme == OPEN { dup := false if !pushedNamespace { dup = true pushedNamespace = true } p.open(dup) } else { stmts = append(stmts, p.statement()) } } p.pop() // pop the rbrace if pushedNamespace { p.popNamespace() // restore the previous namespace if we pushed a new one } if len(stmts) == 0 { stmts = nil } return stmts }
func (p *Parser) literal() (node *tp.Instruction) { token := p.pop() switch token.Lexeme { case STRING: node = tp.MakeText(token.Value, token.LineNumber) case REGEXP: node = tp.MakeFunctionCall("regexp", tp.ListInstructions(tp.MakeText(token.Value, token.LineNumber), tp.MakeText(token.ExtraValue, token.LineNumber)), nil, token.LineNumber) case POS: node = tp.MakePosition(token.Value, token.LineNumber) } return node }
func (p *Parser) cast(typeName *Token) (node *tp.Instruction) { typeNameStr := typeName.Value // grab the function name typeLineNo := typeName.LineNumber if p.peek().Lexeme != LPAREN { p.error("parenthesized argument needed for typecast to " + typeNameStr) } p.pop() // pop the lparen expr := p.expression() if p.peek().Lexeme != RPAREN { p.error("single argument to " + typeNameStr + " typecast is missing closing parenthesis") } p.pop() // pop the rparen var block []*tp.Instruction if p.peek().Lexeme == LBRACE { block = p.block() } node = tp.MakeFunctionCall(typeNameStr, tp.ListInstructions(expr), block, typeLineNo) return node }
func (p *Parser) variable(ns string) (node *tp.Instruction) { // ns = strings.Split(ns, ",")[0] // only use the first namespace -- can't efficiently search namespaces for global vars token := p.pop() lexeme, name, lineNo := token.Lexeme, token.Value, token.LineNumber sigil := "$" if lexeme == LVAR { sigil = "%" } var val *tp.Instruction var block []*tp.Instruction if p.peek().Lexeme == EQUAL { p.pop() // pop the equal sign switch p.peek().Lexeme { case STRING, REGEXP, POS, READ, ID, TYPE, GVAR, LVAR, LPAREN: val = p.expression() default: p.error("invalid expression in assignment to " + sigil + name) } } if p.peek().Lexeme == LBRACE { block = p.block() } if lexeme == LVAR { node = tp.MakeLocalVar(name, val, block, lineNo) } else { fullVarName := name // if the namespace is 'tritium', leave it off -- necessary to avoid breaking all that global capture stuff if ns != "tritium" { fullVarName = fmt.Sprintf("%s.%s", ns, name) } args := tp.ListInstructions(tp.MakeText(fullVarName, lineNo)) if val != nil { args = append(args, val) } node = tp.MakeFunctionCall("var", args, block, lineNo) } return node }
func (p *Parser) expression() (node *tp.Instruction) { terms := tp.ListInstructions(p.term()) for p.peek().Lexeme == PLUS { p.pop() // pop the plus sign switch p.peek().Lexeme { case STRING, REGEXP, POS, READ, ID, TYPE, GVAR, LVAR, LPAREN: rhs := p.term() last := len(terms) - 1 if terms[last].GetType() == constants.Instruction_TEXT && rhs.GetType() == constants.Instruction_TEXT { terms[last] = tp.MakeText(terms[last].GetValue()+rhs.GetValue(), terms[last].GetLineNumber()) } else { terms = append(terms, rhs) } default: p.error("argument to `+` must be a self-contained expression") } } if len(terms) > 1 { node = tp.FoldLeft("concat", terms[0], terms[1:len(terms)]) } else { node = terms[0] } return node }
func (p *Parser) call(funcName *Token) (node *tp.Instruction) { funcNameStr := funcName.Value // grab the function name funcLineNo := funcName.LineNumber if p.peek().Lexeme != LPAREN { p.error("parenthesized argument list expected in call to " + funcNameStr) } p.pop() // pop the lparen ords, kwdnames, kwdvals := p.arguments(funcNameStr) // gather the arguments numArgs := len(ords) // this will never happen because p.arguments() only returns when it encounters an rparen if p.peek().Lexeme != RPAREN { p.error("unterminated argument list in call to " + funcNameStr) } p.pop() // pop the rparen var block []*tp.Instruction if p.peek().Lexeme == LBRACE { block = p.block() } // Expand keyword args if kwdnames != nil && kwdvals != nil { kwdToGensym := make(map[string]string, len(kwdnames)) outer := tp.ListInstructions() for i, k := range kwdnames { tempname := p.gensym() tempvar := tp.MakeFunctionCall("var", tp.ListInstructions(tp.MakeText(tempname, funcLineNo), kwdvals[i]), nil, funcLineNo) outer = append(outer, tempvar) kwdToGensym[k] = tempname } inner := tp.ListInstructions() for _, k := range kwdnames { getter := tp.MakeFunctionCall("var", tp.ListInstructions(tp.MakeText(kwdToGensym[k], funcLineNo)), nil, funcLineNo) setter := tp.MakeFunctionCall("set", tp.ListInstructions(tp.MakeText(k, funcLineNo), getter), nil, funcLineNo) inner = append(inner, setter) } if block != nil { for _, v := range block { inner = append(inner, v) } } theCall := tp.MakeFunctionCall(funcNameStr, ords, inner, funcLineNo) outer = append(outer, theCall) node = tp.MakeBlock(outer, funcLineNo) } else if funcNameStr == "concat" && numArgs > 2 { // expand variadic concat into nested binary concats lhs := tp.FoldLeft("concat", ords[0], ords[1:numArgs-1]) rhs := ords[numArgs-1] node = tp.MakeFunctionCall("concat", tp.ListInstructions(lhs, rhs), block, funcLineNo) } else if funcNameStr == "log" && numArgs > 1 { // expand variadic log into composition of log and concat cats := tp.FoldLeft("concat", ords[0], ords[1:]) node = tp.MakeFunctionCall("log", tp.ListInstructions(cats), block, funcLineNo) } else { node = tp.MakeFunctionCall(funcNameStr, ords, block, funcLineNo) } // if it's not a root file, we can assume that it's a user-called function if !p.CompilingMixer /* p.RootFile == false && IncludeSelectorInfo == true */ { node.IsUserCalled = proto.Bool(true) } return node }