You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
139 lines
3.2 KiB
Go
139 lines
3.2 KiB
Go
package script
|
|
|
|
import (
|
|
"os"
|
|
|
|
"github.com/alecthomas/participle/v2"
|
|
"github.com/alecthomas/participle/v2/lexer"
|
|
)
|
|
|
|
type Program struct {
|
|
Statements []Expression `parser:"Newline? ( @@ ( Newline @@ )* Newline? )?"`
|
|
}
|
|
|
|
type ArgumentExpression interface{}
|
|
|
|
type Expression interface{}
|
|
|
|
type FunctionReceiver interface{}
|
|
|
|
var _ FunctionReceiver = &Identifier{}
|
|
var _ FunctionReceiver = &ParenthesizedExpression{}
|
|
|
|
type FunctionCall struct {
|
|
Receiver FunctionReceiver `parser:"@@"`
|
|
Arguments []ArgumentExpression `parser:"@@+"`
|
|
}
|
|
|
|
type ParenthesizedExpression struct {
|
|
Inner Expression `parser:"'(' @@ ')'"`
|
|
}
|
|
|
|
type ListExpression struct {
|
|
Values []Expression `parser:"'[' ( Newline? @@ ( Newline? @@ )* Newline? )? ']'"`
|
|
}
|
|
|
|
type DictExpression struct {
|
|
Entries []*DictEntry `parser:"'{' ( Newline? @@ ( ( ',' | Newline ) @@ )* Newline? )? '}'"`
|
|
}
|
|
|
|
type DictEntry struct {
|
|
Key DictEntryKey `parser:"@@"`
|
|
Value Expression `parser:"'=' @@"`
|
|
}
|
|
|
|
type DictEntryKey interface{}
|
|
|
|
type Boolean bool
|
|
|
|
func (b *Boolean) Capture(values []string) error {
|
|
*b = values[0] == "true"
|
|
return nil
|
|
}
|
|
|
|
type LiteralBoolean struct {
|
|
Value Boolean `parser:"@('true' | 'false')"`
|
|
}
|
|
|
|
type LiteralString struct {
|
|
Value string `parser:"@String"`
|
|
}
|
|
|
|
type LiteralNumber struct {
|
|
Value float64 `parser:"@Number"`
|
|
}
|
|
|
|
type Identifier struct {
|
|
Value string `parser:"@Ident"`
|
|
}
|
|
|
|
type Quote struct {
|
|
Value Quotation `parser:"'#' @@"`
|
|
}
|
|
|
|
type Quotation interface{}
|
|
|
|
type ExpressionFactor interface{}
|
|
|
|
type BinaryOperation struct {
|
|
Lhs ExpressionFactor `parser:"@@"`
|
|
Operator string `parser:"( @Operator"`
|
|
Rhs Expression `parser:" @@ )?"`
|
|
}
|
|
|
|
var (
|
|
scriptLexer = lexer.MustSimple([]lexer.SimpleRule{
|
|
{Name: "Comment", Pattern: `--[^\n]*\n?`},
|
|
{Name: "String", Pattern: `"(\\"|[^"])*"`},
|
|
{Name: "Number", Pattern: `[-+]?(?:\d*\.)?\d+`},
|
|
{Name: "Ident", Pattern: `[a-zA-Z]\w*`},
|
|
{Name: "Meta", Pattern: `\#`},
|
|
// an operator is any combination of [+-*/%<>=:;,&|^?#@] expect for the string "="
|
|
{Name: "Operator", Pattern: `(=[\+\-\*/%<>=:&|\^?#@]+)|([\+\-\*/%<>:&|\^?#@][\+\-\*/%<>=:&|\^?#@]*)`},
|
|
{Name: "Punct", Pattern: `[\(\)\{\}\[\]=,]`},
|
|
{Name: "Newline", Pattern: `[ \t]*\n\s*`},
|
|
{Name: "Whitespace", Pattern: `[ \t]+`},
|
|
})
|
|
// Parser
|
|
Parser = participle.MustBuild[Program](
|
|
participle.Lexer(scriptLexer),
|
|
participle.Elide("Comment", "Whitespace"),
|
|
participle.Unquote("String"),
|
|
participle.Union[Expression](
|
|
&FunctionCall{},
|
|
&BinaryOperation{},
|
|
),
|
|
participle.Union[ArgumentExpression](
|
|
&BinaryOperation{},
|
|
),
|
|
participle.Union[ExpressionFactor](
|
|
&ListExpression{},
|
|
&DictExpression{},
|
|
&LiteralNumber{},
|
|
&LiteralString{},
|
|
&LiteralBoolean{},
|
|
&ParenthesizedExpression{},
|
|
&Quote{},
|
|
&Identifier{},
|
|
),
|
|
participle.Union[Quotation](
|
|
&Identifier{},
|
|
&ParenthesizedExpression{},
|
|
),
|
|
participle.Union[DictEntryKey](
|
|
&Identifier{},
|
|
&ParenthesizedExpression{},
|
|
),
|
|
participle.Union[FunctionReceiver](
|
|
&Identifier{},
|
|
&ParenthesizedExpression{},
|
|
),
|
|
participle.UseLookahead(2),
|
|
)
|
|
)
|
|
|
|
func Parse(source string) (*Program, error) {
|
|
return Parser.ParseString("", source, participle.Trace(os.Stdout))
|
|
// return Parser.ParseString("", source)
|
|
}
|