Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test PR: Windows debug #331

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/scala.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
distribution: temurin
java-version: 17
cache: sbt
- run: sbt "compile;test;doc;stage"
- run: sbt "project compiler-access" run

styling:
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ project/plugins/project/
# plugin-vscode
plugin-vscode/node_modules
plugin-vscode/package-lock.json
plugin-vscode/tsconfig.tsbuildinfo
*/out
*.vsix
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@

package org.alephium.ralph.lsp.access.compiler

import org.alephium.ralph._
import org.alephium.ralph.lsp.access.compiler.ast.Tree
import org.alephium.ralph.lsp.access.compiler.message.CompilerMessage
import org.alephium.ralph.lsp.access.compiler.message.error.FastParseError
import org.alephium.ralph.lsp.access.compiler.parser.soft.ast.SoftAST
import org.alephium.ralph.lsp.utils.log.ClientLogger
import org.alephium.ralph._
import org.alephium.ralph.lsp.utils.URIUtil

import java.net.URI

Expand All @@ -30,6 +33,10 @@ object CompilerAccess {
def ralphc: CompilerAccess =
RalphCompilerAccess

/** Checks if the URI is of a `*.ral` source file */
def isRalphFileExtension(uri: URI): Boolean =
URIUtil.getFileExtension(uri) == RALPH_FILE_EXTENSION

}

/**
Expand All @@ -41,6 +48,14 @@ object CompilerAccess {
*/
trait CompilerAccess {

/**
* Runs the soft parser.
*
* @param code Code to parse.
* @return Parsing error or successfully parsed [[SoftAST]].
*/
def parseSoft(code: String): Either[FastParseError, SoftAST.BlockBody]

/**
* Runs the parser phase.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import org.alephium.protocol.vm.StatefulContext
import org.alephium.ralph._
import org.alephium.ralph.lsp.access.compiler.ast.Tree
import org.alephium.ralph.lsp.access.compiler.message.CompilerMessage
import org.alephium.ralph.lsp.access.compiler.message.error._
import org.alephium.ralph.lsp.access.compiler.message.error.FastParseError
import org.alephium.ralph.lsp.access.compiler.parser.soft.ast.SoftAST
import org.alephium.ralph.lsp.access.compiler.parser.soft.SoftParser
import org.alephium.ralph.lsp.access.util.TryUtil
import org.alephium.ralph.lsp.utils.log.{ClientLogger, StrictImplicitLogging}

Expand All @@ -36,6 +38,16 @@ import java.net.URI

private object RalphCompilerAccess extends CompilerAccess with StrictImplicitLogging {

/** @inheritdoc */
override def parseSoft(code: String): Either[FastParseError, SoftAST.BlockBody] =
fastparse.parse(code, SoftParser.parse(_)) match {
case Parsed.Success(ast: SoftAST.BlockBody, _) =>
Right(ast)

case failure: Parsed.Failure =>
Left(FastParseError(failure))
}

/** @inheritdoc */
def parseContracts(
fileURI: URI,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@

package org.alephium.ralph.lsp.access.compiler.ast

import org.alephium.ralph.lsp.access.compiler.ast.node.{Node, NodeBuilder}
import org.alephium.ralph.{SourceIndex, Ast}
import org.alephium.ralph.{Ast, SourceIndex}
import org.alephium.ralph.lsp.access.compiler.ast.node.NodeBuilder
import org.alephium.ralph.lsp.utils.Node

/** Ralph Syntax Tree (AST) */
sealed trait Tree {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package org.alephium.ralph.lsp.access.compiler.ast.node
import com.typesafe.scalalogging.StrictLogging
import org.alephium.ralph.Ast
import org.alephium.ralph.Ast.Positioned
import org.alephium.ralph.lsp.utils.Node

/** Functions that build a traversable tree from [[Ast.ContractWithState]], returning the root [[Node]]. */
object NodeBuilder extends StrictLogging {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ object SourceIndexExtra {
fileURI = Some(fileURI)
)

/**
* Creates a [[SourceIndex]] when the start and end indexes are known.
*
* @param from the starting index of the range (inclusive)
* @param to the ending index of the range (exclusive)
*/
@inline def range(
from: Int,
to: Int): SourceIndex =
SourceIndex(
index = from,
width = to - from,
fileURI = None
)

/**
* Sending negative index to the client would be incorrect.
* This set the index to be an empty range.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2024 The Alephium Authors
// This file is part of the alephium project.
//
// The library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the library. If not, see http://www.gnu.org/licenses/.

package org.alephium.ralph.lsp.access.compiler.parser.soft

import fastparse._
import fastparse.NoWhitespace.noWhitespaceImplicit
import org.alephium.ralph.lsp.access.compiler.message.SourceIndexExtra.range
import org.alephium.ralph.lsp.access.compiler.parser.soft.CommonParser._
import org.alephium.ralph.lsp.access.compiler.parser.soft.ast.{SoftAST, Token}

private object BlockParser {

def clause[Unknown: P](mandatory: Boolean): P[SoftAST.BlockClause] =
P(Index ~ TokenParser.openCurly(mandatory) ~ spaceOrFail.? ~ body(Some(Token.CloseCurly.lexeme.head)) ~ spaceOrFail.? ~ TokenParser.closeCurly ~ Index) map {
case (from, openCurly, preBodySpace, body, postBodySpace, closeCurly, to) =>
SoftAST.BlockClause(
index = range(from, to),
openCurly = openCurly,
preBodySpace = preBodySpace,
body = body,
postBodySpace = postBodySpace,
closeCurly = closeCurly
)
}

def body[Unknown: P]: P[SoftAST.BlockBody] =
body(stopChar = None)

private def body[Unknown: P](stopChar: Option[Char]): P[SoftAST.BlockBody] =
P(Index ~ spaceOrFail.? ~ bodyPart(stopChar).rep ~ Index) map {
case (from, headSpace, parts, to) =>
SoftAST.BlockBody(
index = range(from, to),
prePartsSpace = headSpace,
parts = parts
)
}

private def bodyPart[Unknown: P](stopChar: Option[Char]): P[SoftAST.BlockBodyPart] =
P(Index ~ part(stopChar) ~ spaceOrFail.? ~ Index) map {
case (from, ast, space, to) =>
SoftAST.BlockBodyPart(
index = range(from, to),
part = ast,
postPartSpace = space
)
}

private def part[Unknown: P](stopChar: Option[Char]): P[SoftAST.BodyPartAST] =
P {
TemplateParser.parse |
FunctionParser.parse |
CommentParser.parse |
unresolved(stopChar)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2024 The Alephium Authors
// This file is part of the alephium project.
//
// The library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the library. If not, see http://www.gnu.org/licenses/.

package org.alephium.ralph.lsp.access.compiler.parser.soft

import fastparse._
import fastparse.NoWhitespace.noWhitespaceImplicit
import org.alephium.ralph.lsp.access.compiler.message.SourceIndexExtra.range
import org.alephium.ralph.lsp.access.compiler.parser.soft.CommonParser._
import org.alephium.ralph.lsp.access.compiler.parser.soft.ast.SoftAST

private object CommentParser {

def parse[Unknown: P]: P[SoftAST.Comment] =
P(Index ~ TokenParser.doubleForwardSlash ~ spaceOrFail.? ~ text.? ~ Index) map {
case (from, doubleForwardSlash, space, text, to) =>
SoftAST.Comment(
index = range(from, to),
doubleForwardSlash = doubleForwardSlash,
space = space,
text = text
)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright 2024 The Alephium Authors
// This file is part of the alephium project.
//
// The library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the library. If not, see http://www.gnu.org/licenses/.

package org.alephium.ralph.lsp.access.compiler.parser.soft

import fastparse._
import fastparse.NoWhitespace.noWhitespaceImplicit
import org.alephium.ralph.lsp.access.compiler.message.SourceIndexExtra.range
import org.alephium.ralph.lsp.access.compiler.parser.soft.ast.{SoftAST, Token}

private object CommonParser {

def spaceOrFail[Unknown: P]: P[SoftAST.Space] =
P(Index ~ CharsWhileIn(" \t\r\n").! ~ Index) map {
case (from, text, to) =>
SoftAST.Space(
code = text,
index = range(from, to)
)
}

def space[Unknown: P]: P[SoftAST.SpaceAST] =
P(Index ~ spaceOrFail.?) map {
case (_, Some(space)) =>
space

case (from, None) =>
SoftAST.SpaceExpected(range(from, from))
}

def text[Unknown: P]: P[SoftAST.Text] =
P(Index ~ CharsWhileNot(Token.Newline.lexeme).! ~ Index) map {
case (from, text, to) =>
SoftAST.Text(
code = text,
index = range(from, to)
)
}

def unresolved[Unknown: P](stopChar: Option[Char]): P[SoftAST.Unresolved] =
P(Index ~ CharsWhileNot(Token.Space.lexeme ++ Token.Newline.lexeme ++ stopChar).! ~ Index) map {
case (from, text, to) =>
SoftAST.Unresolved(
code = text,
index = range(from, to)
)
}

def identifier[Unknown: P]: P[SoftAST.IdentifierAST] =
P(Index ~ isLetterDigitOrUnderscore.!.? ~ Index) map {
case (from, Some(identifier), to) =>
SoftAST.Identifier(
code = identifier,
index = range(from, to)
)

case (from, None, to) =>
SoftAST.IdentifierExpected(range(from, to))
}

def typeName[Unknown: P]: P[SoftAST.SingleTypeAST] =
P(Index ~ isLetterDigitOrUnderscore.!.? ~ Index) map {
case (from, Some(typeName), to) =>
SoftAST.Type(
code = typeName,
index = range(from, to)
)

case (from, None, to) =>
SoftAST.TypeExpected(range(from, to))
}

private def CharsWhileNot[Unknown: P](chars: String): P[Unit] =
CharsWhile {
char =>
!(chars contains char)
}

private def isLetterDigitOrUnderscore[Unknown: P]: P[Unit] =
CharsWhile {
char =>
char.isLetterOrDigit || char == Token.Underscore.lexeme.head
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.alephium.ralph.lsp.access.compiler.parser.soft

import org.alephium.ralph.lsp.access.compiler.CompilerAccess

object Demo extends App {

val compiler =
CompilerAccess.ralphc

val ast =
compiler.parseSoft {
"""
|Contract HelloWorld(type: SomeType, tuple: (A, B)) {
|
| fn function(nested_tuple: (A, (B, C))) -> ABC {
|
| }
|
| 🚀
|}
|""".stripMargin
} match {
case Right(softAST) =>
softAST

case Left(parseError) =>
// Format and print the FastParse error.
// The goal here is to ensure that the parser always succeeds, regardless of the input.
// Therefore, this error should never occur. If an error does occur, the parser should be updated to handle those cases.
println(parseError.error.toFormatter().format(Some(Console.RED)))
throw parseError.error
}

// Emit code generated from AST
println("Parsed code:")
println(ast.toCode())

// Emit parsed AST
println("SoftAST:")
println(ast.toStringTree())

}
Loading