Exemple #1
0
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
}
Exemple #2
0
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
}
Exemple #3
0
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
}
Exemple #4
0
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
}
Exemple #5
0
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
}
Exemple #6
0
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
}
Exemple #7
0
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
}