Skip to content

Commit a90cdca

Browse files
committed
stdlib/compiler: alias imports, dicts #9 #32
- compiler: alias imports `import alias = module.name` #32 - compiler: dictionary literals `[:]` and `["a": "b"]` #9 - stdlib: new type `prelude.Dict` #9 - stdlib: new type `prelude.Pair`
1 parent 50431f8 commit a90cdca

18 files changed

+418
-7
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## v0.0.17-next
44

5+
- compiler: alias imports `import alias = module.name` #32
6+
- compiler: dictionary literals `[:]` and `["a": "b"]` #9
7+
- stdlib: new type `prelude.Dict` #9
8+
- stdlib: new type `prelude.Pair`
59
- stdlib: Float-support! #24
610
- stdlib: `!` operator #22
711

ast/decl-import.go

+14-2
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ var _ Decl = DeclImport{}
99
var _ Overviewable = DeclImport{}
1010

1111
type DeclImport struct {
12+
Alias Identifier
1213
ModuleName ModuleName
1314
Members []DeclImportMember
1415

1516
MetaInfo *MetaDecl
1617
}
1718

1819
func (e DeclImport) DeclName() Identifier {
19-
segments := strings.Split(string(e.ModuleName), ".")
20-
return Identifier(segments[len(segments)-1])
20+
return e.Alias
2121
}
2222

2323
func (e DeclImport) DeclOverview() string {
@@ -37,7 +37,19 @@ func (e *DeclImport) AddMember(member DeclImportMember) {
3737
}
3838

3939
func MakeDeclImport(name ModuleName, source *Source) *DeclImport {
40+
segments := strings.Split(string(name), ".")
41+
alias := Identifier(segments[len(segments)-1])
4042
return &DeclImport{
43+
Alias: alias,
44+
ModuleName: name,
45+
Members: make([]DeclImportMember, 0),
46+
MetaInfo: &MetaDecl{source},
47+
}
48+
}
49+
50+
func MakeDeclAliasImport(alias Identifier, name ModuleName, source *Source) *DeclImport {
51+
return &DeclImport{
52+
Alias: alias,
4153
ModuleName: name,
4254
Members: make([]DeclImportMember, 0),
4355
MetaInfo: &MetaDecl{source},

ast/expr-dict.go

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package ast
2+
3+
var _ Expr = ExprDict{}
4+
5+
type ExprDict struct {
6+
Entries []ExprDictEntry
7+
8+
MetaInfo *MetaExpr
9+
}
10+
11+
func (e ExprDict) Meta() *MetaExpr {
12+
return e.MetaInfo
13+
}
14+
15+
func MakeExprDict(entries []ExprDictEntry, source *Source) *ExprDict {
16+
return &ExprDict{
17+
Entries: entries,
18+
MetaInfo: &MetaExpr{
19+
Source: source,
20+
},
21+
}
22+
}
23+
24+
func (e ExprDict) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
25+
for _, el := range e.Entries {
26+
el.Key.EnumerateNestedDecls(enumerate)
27+
el.Value.EnumerateNestedDecls(enumerate)
28+
}
29+
}
30+
31+
type ExprDictEntry struct {
32+
Key Expr
33+
Value Expr
34+
MetaInfo *MetaExpr
35+
}
36+
37+
func MakeExprDictEntry(key Expr, value Expr, source *Source) *ExprDictEntry {
38+
return &ExprDictEntry{
39+
Key: key,
40+
Value: value,
41+
MetaInfo: &MetaExpr{
42+
Source: source,
43+
},
44+
}
45+
}

parser/node-types.go

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ var (
2727
TYPE_NODE_INT_LITERAL = "int_literal"
2828
TYPE_NODE_FLOAT_LITERAL = "float_literal"
2929
TYPE_NODE_ARRAY_LITERAL = "array_literal"
30+
TYPE_NODE_DICT_LITERAL = "dict_literal"
31+
TYPE_NODE_DICT_ENTRY = "dict_entry"
3032
TYPE_NODE_FUNCTION_LITERAL = "function_literal"
3133
TYPE_NODE_PARAMETER_LIST = "parameter_list"
3234
TYPE_NODE_IDENTIFIER = "identifier"

parser/parse-decl-import.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
func (fp *FileParser) ParseImportDeclaration() (*ast.DeclImport, []SyntaxError) {
1010
importModuleNode := fp.Node.ChildByFieldName("name")
11+
aliasNode := fp.Node.ChildByFieldName("alias")
1112
membersNode := fp.Node.ChildByFieldName("members")
1213
var membersCount int
1314
if membersNode == nil {
@@ -21,7 +22,13 @@ func (fp *FileParser) ParseImportDeclaration() (*ast.DeclImport, []SyntaxError)
2122
modulePath = append(modulePath, importModuleNode.NamedChild(i).Content(fp.Source))
2223
}
2324
moduleName := ast.ModuleName(strings.Join(modulePath, "."))
24-
importDecl := ast.MakeDeclImport(moduleName, fp.AstSource())
25+
26+
var importDecl *ast.DeclImport
27+
if aliasNode == nil {
28+
importDecl = ast.MakeDeclImport(moduleName, fp.AstSource())
29+
} else {
30+
importDecl = ast.MakeDeclAliasImport(ast.Identifier(aliasNode.Content(fp.Source)), moduleName, fp.AstSource())
31+
}
2532

2633
for i := 0; i < membersCount; i++ {
2734
child := membersNode.NamedChild(i)

parser/parse-expr-dict.go

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package parser
2+
3+
import (
4+
"github.com/vknabel/lithia/ast"
5+
)
6+
7+
func (fp *FileParser) ParseExprDict() (*ast.ExprDict, []SyntaxError) {
8+
numberOfEntries := int(fp.Node.NamedChildCount())
9+
entries := make([]ast.ExprDictEntry, 0, numberOfEntries)
10+
for i := 0; i < numberOfEntries; i++ {
11+
entryNode := fp.Node.NamedChild(i)
12+
if entryNode.Type() == TYPE_NODE_COMMENT {
13+
fp.Comments = append(fp.Comments, entryNode.Content(fp.Source))
14+
continue
15+
}
16+
entry, errs := fp.ChildParserConsumingComments(entryNode).parseExprDictEntry()
17+
if len(errs) > 0 {
18+
return nil, errs
19+
}
20+
if entry != nil {
21+
entries = append(entries, *entry)
22+
}
23+
}
24+
return ast.MakeExprDict(entries, fp.AstSource()), nil
25+
}
26+
27+
func (fp *FileParser) parseExprDictEntry() (*ast.ExprDictEntry, []SyntaxError) {
28+
keyNode := fp.Node.ChildByFieldName("key")
29+
valueNode := fp.Node.ChildByFieldName("value")
30+
31+
keyP := fp.ChildParserConsumingComments(keyNode)
32+
key, errs := keyP.ParseExpression()
33+
if len(errs) > 0 {
34+
return nil, errs
35+
}
36+
37+
valueP := fp.ChildParserConsumingComments(valueNode)
38+
value, errs := valueP.ParseExpression()
39+
if len(errs) > 0 {
40+
return nil, errs
41+
}
42+
return ast.MakeExprDictEntry(key, value, fp.AstSource()), nil
43+
}

parser/parse-expr.go

+7
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ func (fp *FileParser) ParseExpressionIfGiven() (ast.Expr, []SyntaxError) {
8585
} else {
8686
return expr, errs
8787
}
88+
case TYPE_NODE_DICT_LITERAL:
89+
expr, errs := fp.ParseExprDict()
90+
if expr == nil {
91+
return nil, errs
92+
} else {
93+
return expr, errs
94+
}
8895
case TYPE_NODE_FUNCTION_LITERAL:
8996
expr, errs := fp.ParseFunctionExpr(fmt.Sprintf("func#%d", fp.CountFunction()))
9097
if expr == nil {

runtime/environment-data.go

+15
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,18 @@ func (env *Environment) MakeEagerList(slice []RuntimeValue) (DataRuntimeValue, *
4545
})
4646
}
4747
}
48+
49+
func (env *Environment) MakeSome(value Evaluatable) (DataRuntimeValue, *RuntimeError) {
50+
return env.MakeDataRuntimeValue("Some", map[string]Evaluatable{
51+
"value": value,
52+
})
53+
}
54+
func (env *Environment) MakeNone() (DataRuntimeValue, *RuntimeError) {
55+
return env.MakeEmptyDataRuntimeValue("None")
56+
}
57+
func (env *Environment) MakePair(key Evaluatable, value Evaluatable) (DataRuntimeValue, *RuntimeError) {
58+
return env.MakeDataRuntimeValue("Pair", map[string]Evaluatable{
59+
"key": key,
60+
"value": value,
61+
})
62+
}

runtime/evaluatable-expr.go

+18
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ func (e EvaluatableExpr) Evaluate() (RuntimeValue, *RuntimeError) {
4141
switch expr := e.Expr.(type) {
4242
case *ast.ExprArray:
4343
return e.EvaluateExprArray(*expr)
44+
case *ast.ExprDict:
45+
return e.EvaluateExprDict(*expr)
4446
case *ast.ExprFloat:
4547
return e.EvaluateExprFloat(*expr)
4648
case *ast.ExprFunc:
@@ -80,6 +82,22 @@ func (e EvaluatableExpr) EvaluateExprArray(expr ast.ExprArray) (RuntimeValue, *R
8082
return list, err.CascadeExpr(expr)
8183
}
8284

85+
func (e EvaluatableExpr) EvaluateExprDict(expr ast.ExprDict) (RuntimeValue, *RuntimeError) {
86+
rawDict := make(map[PreludeString]Evaluatable)
87+
for _, entry := range expr.Entries {
88+
keyValue, err := MakeEvaluatableExpr(e.Context, entry.Key).Evaluate()
89+
if err != nil {
90+
return nil, err.CascadeExpr(expr)
91+
}
92+
if key, ok := keyValue.(PreludeString); ok {
93+
rawDict[key] = MakeEvaluatableExpr(e.Context, entry.Value)
94+
} else {
95+
return nil, NewRuntimeErrorf("dict key must be a string, got %s", keyValue).CascadeExpr(expr)
96+
}
97+
}
98+
return MakePreludeDict(e.Context, rawDict), nil
99+
}
100+
83101
func (e EvaluatableExpr) EvaluateExprFloat(expr ast.ExprFloat) (RuntimeValue, *RuntimeError) {
84102
return PreludeFloat(expr.Literal), nil
85103
}

runtime/external-prelude.go

+9
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ func (e ExternalPrelude) Lookup(name string, env *Environment, decl ast.Decl) (R
3939
} else {
4040
return nil, false
4141
}
42+
case "Dict":
43+
if externDecl, ok := decl.(ast.DeclExternType); ok {
44+
return PreludePrimitiveExternType{&externDecl, func(value RuntimeValue) (bool, *RuntimeError) {
45+
_, ok := value.(PreludeDict)
46+
return ok, nil
47+
}}, true
48+
} else {
49+
return nil, false
50+
}
4251
case "Char":
4352
if externDecl, ok := decl.(ast.DeclExternType); ok {
4453
return PreludePrimitiveExternType{&externDecl, func(value RuntimeValue) (bool, *RuntimeError) {

runtime/prelude-dict.go

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package runtime
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
var _ RuntimeValue = PreludeDict{}
9+
var PreludeDictTypeRef = MakeRuntimeTypeRef("Dict", "prelude")
10+
11+
type PreludeDict struct {
12+
dict map[PreludeString]Evaluatable
13+
context *InterpreterContext
14+
}
15+
16+
func MakePreludeDict(context *InterpreterContext, dict map[PreludeString]Evaluatable) PreludeDict {
17+
copy := map[PreludeString]Evaluatable{}
18+
for k, v := range dict {
19+
copy[k] = v
20+
}
21+
return PreludeDict{
22+
dict: copy,
23+
context: context,
24+
}
25+
}
26+
27+
func (PreludeDict) RuntimeType() RuntimeTypeRef {
28+
return PreludeDictTypeRef
29+
}
30+
31+
func (rv PreludeDict) String() string {
32+
if len(rv.dict) == 0 {
33+
return "[:]"
34+
}
35+
entries := []string{}
36+
for k, v := range rv.dict {
37+
value, err := v.Evaluate()
38+
if err != nil {
39+
entries = append(entries, fmt.Sprintf("%s: %s", k, err.Error()))
40+
} else {
41+
entries = append(entries, fmt.Sprintf("%s: %s", k, value))
42+
}
43+
}
44+
return fmt.Sprintf("[%s]", strings.Join(entries, ", "))
45+
}
46+
47+
func (rv PreludeDict) Lookup(member string) (Evaluatable, *RuntimeError) {
48+
switch member {
49+
case "length":
50+
return NewConstantRuntimeValue(PreludeInt(len(rv.dict))), nil
51+
case "get":
52+
return NewConstantRuntimeValue(MakeAnonymousFunction(
53+
"get",
54+
[]string{"key"},
55+
func(args []Evaluatable) (RuntimeValue, *RuntimeError) {
56+
key, err := args[0].Evaluate()
57+
if err != nil {
58+
return nil, err
59+
}
60+
if key, ok := key.(PreludeString); ok {
61+
if value, ok := rv.dict[key]; ok {
62+
return rv.context.environment.MakeSome(value)
63+
} else {
64+
return rv.context.environment.MakeNone()
65+
}
66+
} else {
67+
return nil, NewRuntimeErrorf("dict key must be a string, got %s", key)
68+
}
69+
})), nil
70+
case "set":
71+
return NewConstantRuntimeValue(MakeAnonymousFunction(
72+
"set",
73+
[]string{"key", "value"},
74+
func(args []Evaluatable) (RuntimeValue, *RuntimeError) {
75+
key, err := args[0].Evaluate()
76+
if err != nil {
77+
return nil, err
78+
}
79+
80+
if key, ok := key.(PreludeString); ok {
81+
copy := rv.copy()
82+
copy.dict[key] = args[1]
83+
return copy, nil
84+
} else {
85+
return nil, NewRuntimeErrorf("dict key must be a string, got %s", key)
86+
}
87+
})), nil
88+
case "delete":
89+
return NewConstantRuntimeValue(MakeAnonymousFunction(
90+
"delete",
91+
[]string{"key"},
92+
func(args []Evaluatable) (RuntimeValue, *RuntimeError) {
93+
key, err := args[0].Evaluate()
94+
if err != nil {
95+
return nil, err
96+
}
97+
98+
if key, ok := key.(PreludeString); ok {
99+
copy := rv.copy()
100+
delete(copy.dict, key)
101+
return copy, nil
102+
} else {
103+
return nil, NewRuntimeErrorf("dict key must be a string, got %s", key)
104+
}
105+
})), nil
106+
case "entries":
107+
pairs := make([]RuntimeValue, 0, len(rv.dict))
108+
for k, v := range rv.dict {
109+
pair, err := rv.context.environment.MakePair(NewConstantRuntimeValue(k), v)
110+
if err != nil {
111+
return nil, err
112+
}
113+
pairs = append(pairs, pair)
114+
}
115+
dataList, err := rv.context.environment.MakeEagerList(pairs)
116+
return NewConstantRuntimeValue(dataList), err
117+
case "keys":
118+
keys := make([]RuntimeValue, 0, len(rv.dict))
119+
for k := range rv.dict {
120+
keys = append(keys, k)
121+
}
122+
dataList, err := rv.context.environment.MakeEagerList(keys)
123+
return NewConstantRuntimeValue(dataList), err
124+
case "values":
125+
values := make([]Evaluatable, 0, len(rv.dict))
126+
for _, v := range rv.dict {
127+
values = append(values, v)
128+
}
129+
dataList, err := rv.context.environment.MakeList(values)
130+
return NewConstantRuntimeValue(dataList), err
131+
default:
132+
return nil, NewRuntimeErrorf("no such member: %s", member)
133+
}
134+
}
135+
136+
func (rv PreludeDict) copy() PreludeDict {
137+
return MakePreludeDict(rv.context, rv.dict)
138+
}

0 commit comments

Comments
 (0)