Skip to content

Commit

Permalink
wip: Tree-sitter grammar: support Enums
Browse files Browse the repository at this point in the history
  • Loading branch information
OceanOak committed Apr 22, 2024
1 parent 61a221c commit 7e695b2
Show file tree
Hide file tree
Showing 12 changed files with 1,205 additions and 15 deletions.
21 changes: 21 additions & 0 deletions backend/testfiles/execution/stdlib/parser.dark
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,20 @@ module TextToTextRoundtripping =
hasPet: Bool
pet: Pet }"""

// Enum type
("type Color = | Red | Green | Blue" |> roundtripCliScript) = "type Color =\n | Red\n | Green\n | Blue"
("type Color =\n | Red\n | Green\n | Blue" |> roundtripCliScript) = "type Color =\n | Red\n | Green\n | Blue"

("type MyEnum = | A of Int64" |> roundtripCliScript) = "type MyEnum =\n | A of Int64"
("type MyEnum = | A of Int64 * Int64" |> roundtripCliScript) = "type MyEnum =\n | A of Int64 * Int64"
("type MyEnum = | A of x:Int64 * y:Int64" |> roundtripCliScript) = "type MyEnum =\n | A of x: Int64 * y: Int64"
("type MyEnum = | A of Int64 * Bool * String" |> roundtripCliScript) = "type MyEnum =\n | A of Int64 * Bool * String"
("type MyEnum = | A of x: Int64" |> roundtripCliScript) = "type MyEnum =\n | A of x: Int64"
("type MyEnum =\n | A of Int64\n | B of String" |> roundtripCliScript) = "type MyEnum =\n | A of Int64\n | B of String"
("type MyEnum =\n | A of x: Int64\n | B of String" |> roundtripCliScript) = "type MyEnum =\n | A of x: Int64\n | B of String"



module Expr =
// units
("()" |> roundtripCliScript) = "()"
Expand Down Expand Up @@ -330,6 +344,13 @@ module TextToTextRoundtripping =
("Person {name =\"John\"; age = 30L; hasPet = true; pet = Pet {name = \"Luna\"}} "
|> roundtripCliScript) = "Person { name = \"John\"; age = 30L; hasPet = true; pet = Pet { name = \"Luna\" } }"

// enum literal
("Color.Red" |> roundtripCliScript) = "Color.Red"
("Stdlib.Option.None" |> roundtripCliScript) = "Stdlib.Option.None"
("PACKAGE.Darklang.Stdlib.Option.Option.None" |> roundtripCliScript) = "PACKAGE.Darklang.Stdlib.Option.Option.None"
("PACKAGE.Darklang.Stdlib.Option.Option.Some 1L" |> roundtripCliScript) = "PACKAGE.Darklang.Stdlib.Option.Option.Some(1L)"


// variables and let bindings
("assumedlyAVariableName" |> roundtripCliScript) = "assumedlyAVariableName"
// TODO: this is ugly
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ module Darklang =
"parameter" // [7] for function parameter identifiers
"variable" // [8] for general identifiers
"function" // [9] for function names/identifiers
"property" ] // [10] for field names
"property" // [10] for field names
"enumCase" ] // [11] for enum case names

let tokenModifiers = []

Expand All @@ -53,6 +54,7 @@ module Darklang =
| FunctionName -> 9UL

| Property -> 10UL
| EnumCase -> 11UL

let toRelativeTokens
(tokens: List<LanguageTools.SemanticTokens.SemanticToken>)
Expand Down
107 changes: 107 additions & 0 deletions packages/darklang/languageTools/parser.dark
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,71 @@ module Darklang =
(WrittenTypes.TypeDeclaration.Definition.Record recordFields)
|> Stdlib.Result.Result.Ok


| [ child ] when child.typ == "type_decl_def_enum" ->
let enumCases =
match findNodeByFieldName child "content" with
| Some contentNode ->
contentNode.children
|> Stdlib.List.map (fun caseNode ->
let caseNameNode =
(findNodeByFieldName caseNode "case_name")
|> Stdlib.Option.toResult
"No case_name node found in type_decl_enum_case"

let fields =
caseNode.children
|> Stdlib.List.filter (fun field ->
field.typ == "type_decl_enum_field")
|> Stdlib.List.map (fun field ->
let fieldTypeNode =
(findNodeByFieldName field "type")
|> Stdlib.Option.toResult
"No type node found in type_decl_enum_field"

match fieldTypeNode with
| Ok fieldTypeNode ->
let fieldType = TypeReference.parse fieldTypeNode
let labelNode = findNodeByFieldName field "identifier"

let label =
labelNode
|> Stdlib.Option.map (fun ln ->
(ln.sourceRange, ln.text))

match fieldType with
| Ok fieldType ->
WrittenTypes.TypeDeclaration.EnumField
{ range = field.sourceRange
typ = fieldType
label = label
description = "" }
| Error _ ->
(WrittenTypes.Unparseable { source = field })
| Error _ -> (WrittenTypes.Unparseable { source = field }))

match caseNameNode with
| Ok caseNameNode ->
(WrittenTypes.TypeDeclaration.EnumCase
{ range = caseNode.sourceRange
name = (caseNameNode.sourceRange, caseNameNode.text)
fields = fields
description = "" })
|> Stdlib.Result.Result.Ok
| Error _ ->
(WrittenTypes.Unparseable { source = caseNode })
|> Stdlib.Result.Result.Error)
|> Stdlib.Result.values
| None -> []

match enumCases with
| Ok enumCases ->
(WrittenTypes.TypeDeclaration.Definition.Enum enumCases)
|> Stdlib.Result.Result.Ok
| Error _ ->
(WrittenTypes.Unparseable { source = node })
|> Stdlib.Result.Result.Error

| _ ->
(WrittenTypes.Unparseable { source = node })
|> Stdlib.Result.Result.Error
Expand Down Expand Up @@ -1259,6 +1324,46 @@ module Darklang =
|> Stdlib.Result.Result.Error


let parseEnumLiteral
(node: ParsedNode)
: Stdlib.Result.Result<WrittenTypes.Expr, WrittenTypes.Unparseable> =
if node.typ == "enum_literal" then
let typeNameNode =
(findNodeByFieldName node "type_name")
|> Stdlib.Option.toResult "No type_name node found in enum_literal"

let symbolDotNode =
(findNodeByFieldName node "symbol_dot")
|> Stdlib.Option.toResult "No symbol_dot node found in enum_literal"

let caseNameNode =
(findNodeByFieldName node "case_name")
|> Stdlib.Option.toResult "No case_name node found in enum_literal"

let enumFieldsNode =
match findNodeByFieldName node "enum_field" with
| Some enumField ->
match Expr.parse enumField with
| Ok expr -> [ expr ]
| Error _ -> []
| None -> []

match typeNameNode, symbolDotNode, caseNameNode with
| Ok typeNameNode, Ok symbolDotNode, Ok caseNameNode ->

(WrittenTypes.Expr.EEnum(
node.sourceRange,
(typeNameNode.sourceRange, [ typeNameNode.text ]),
(caseNameNode.sourceRange, caseNameNode.text),
enumFieldsNode,
symbolDotNode.sourceRange
))
|> Stdlib.Result.Result.Ok

| _ ->
(WrittenTypes.Unparseable { source = node })
|> Stdlib.Result.Result.Error


let parseLetExpr
(node: ParsedNode)
Expand Down Expand Up @@ -1547,6 +1652,8 @@ module Darklang =

| "record_literal" -> parseRecordLiteral node

| "enum_literal" -> parseEnumLiteral node

// assigning and accessing variables
| "let_expression" -> parseLetExpr node
| "variable_identifier" ->
Expand Down
57 changes: 57 additions & 0 deletions packages/darklang/languageTools/semanticTokens.dark
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,34 @@ module Darklang =
|> Stdlib.List.flatten)
|> Stdlib.List.flatten

| Enum cases ->
cases
|> Stdlib.List.map (fun case ->
let (enumCaseNameRange, _) = case.name

let typeRange =
case.fields
|> Stdlib.List.map (fun field -> TypeReference.tokenize field.typ)

let labelRanges =
case.fields
|> Stdlib.List.map (fun field ->
match field.label with
| Some((range, _)) -> [ range ]
| None -> [])

let labelTokens =
labelRanges
|> Stdlib.List.flatten
|> Stdlib.List.map (fun range -> makeToken range TokenType.Property)

[ [ makeToken enumCaseNameRange TokenType.EnumCase ]
labelTokens
typeRange |> Stdlib.List.flatten ]
|> Stdlib.List.flatten)
|> Stdlib.List.flatten


// type ID = UInt64
let tokenize
(t: WrittenTypes.TypeDeclaration.TypeDeclaration)
Expand Down Expand Up @@ -382,6 +410,35 @@ module Darklang =
[ makeToken symbolCloseBrace TokenType.Symbol ] ]
|> Stdlib.List.flatten


// Option.Option.Some 1L
| EEnum(range, typeName, caseName, fields, symbolDot) ->
let typeName =
match typeName with
| (range, _) -> [ makeToken range TokenType.TypeName ]
| _ -> []

let caseName =
match caseName with
| (range, _) -> [ makeToken range TokenType.EnumCase ]
| _ -> []

let fields =
fields
|> Stdlib.List.map (fun expr -> Expr.tokenize expr)
|> Stdlib.List.flatten

[ // Option.Option.Some
typeName
// .
[ makeToken symbolDot TokenType.Symbol ]
// Some
caseName
// 1L
fields ]
|> Stdlib.List.flatten


// let x = 2
// x + 1
| ELet(range, lp, expr, body, keywordLet, symbolEquals) ->
Expand Down
20 changes: 20 additions & 0 deletions packages/darklang/languageTools/writtenTypes.dark
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,22 @@ module Darklang =
description: String
symbolColon: SourceRange }

type EnumField =
{ range: SourceRange
typ: TypeReference.TypeReference
label: Stdlib.Option.Option<SourceRange * String>
description: String }

type EnumCase =
{ range: SourceRange
name: SourceRange * String
fields: List<EnumField>
description: String }

type Definition =
| Alias of TypeReference.TypeReference
| Record of List<RecordField>
| Enum of List<EnumCase>

type TypeDeclaration =
{ range: SourceRange
Expand Down Expand Up @@ -217,6 +230,13 @@ module Darklang =
symbolOpenBrace: SourceRange *
symbolCloseBrace: SourceRange

| EEnum of
SourceRange *
typeName: (SourceRange * List<String>) *
caseName: (SourceRange * String) *
fields: List<Expr> *
symbolDot: SourceRange

| ELet of
SourceRange *
LetPattern *
Expand Down
51 changes: 51 additions & 0 deletions packages/darklang/languageTools/writtenTypesToProgramTypes.dark
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,38 @@ module Darklang =
typ = TypeReference.toPT resolver f.typ
description = "" }

module EnumField =
let toPT
(resolver: NameResolver.NameResolutionSettings)
(f: WrittenTypes.TypeDeclaration.EnumField)
: ProgramTypes.TypeDeclaration.EnumField =

let label =
match f.label with
| Some l -> Stdlib.Option.Option.Some(l |> Stdlib.Tuple2.second)
| None -> Stdlib.Option.Option.None

ProgramTypes.TypeDeclaration.EnumField
{ typ = TypeReference.toPT resolver f.typ
label = label
description = "" }

module EnumCase =
let toPT
(resolver: NameResolver.NameResolutionSettings)
(c: WrittenTypes.TypeDeclaration.EnumCase)
: ProgramTypes.TypeDeclaration.EnumCase =

let name = c.name |> Stdlib.Tuple2.second

let fields =
Stdlib.List.map c.fields (fun f -> EnumField.toPT resolver f)

ProgramTypes.TypeDeclaration.EnumCase
{ name = name
fields = fields
description = "" }

module Definition =
let toPT
(resolver: NameResolver.NameResolutionSettings)
Expand All @@ -147,6 +179,10 @@ module Darklang =

ProgramTypes.TypeDeclaration.Definition.Record fields

| Enum cases ->
let cases = Stdlib.List.map cases (fun c -> EnumCase.toPT resolver c)
ProgramTypes.TypeDeclaration.Definition.Enum cases

let toPT
(resolver: NameResolver.NameResolutionSettings)
(d: WrittenTypes.TypeDeclaration.TypeDeclaration)
Expand Down Expand Up @@ -277,6 +313,21 @@ module Darklang =

ProgramTypes.Expr.ERecord(gid (), typeName, fields)

| EEnum(_, typeName, caseName, fields, _) ->
let sr = Stdlib.Tuple2.first typeName
let unresolvedTypeName = Stdlib.Tuple2.second typeName

let typeName =
NameResolver.TypeName.resolve
resolver
[]
(WrittenTypes.Name.Unresolved sr unresolvedTypeName)

let caseName = caseName |> Stdlib.Tuple2.second
let fields = Stdlib.List.map fields (fun expr -> toPT resolver expr)

ProgramTypes.Expr.EEnum(gid (), typeName, caseName, fields)

// declaring and accessing variables
| ELet(_, pat, rhs, body, _, _) ->
ProgramTypes.Expr.ELet(
Expand Down
Loading

0 comments on commit 7e695b2

Please sign in to comment.