mirror of
https://github.com/enso-org/enso.git
synced 2024-12-22 21:41:34 +03:00
Wrapping parser for Rust (#325)
* deriving JSON Encoder for Scala AST types * websocket-based Parser Service * wrapper for Parser in Rust that includes client for Parser Service
This commit is contained in:
parent
112f0c6c39
commit
6078b54f50
6
.gitignore
vendored
6
.gitignore
vendored
@ -20,6 +20,12 @@ target/
|
|||||||
*.class
|
*.class
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
|
##########
|
||||||
|
## Rust ##
|
||||||
|
##########
|
||||||
|
|
||||||
|
Cargo.lock
|
||||||
|
|
||||||
#############
|
#############
|
||||||
## Haskell ##
|
## Haskell ##
|
||||||
#############
|
#############
|
||||||
|
5
Cargo.toml
Normal file
5
Cargo.toml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[workspace]
|
||||||
|
|
||||||
|
members = [
|
||||||
|
"common/rust/parser"
|
||||||
|
]
|
22
build.sbt
22
build.sbt
@ -10,6 +10,7 @@ import sbtassembly.AssemblyPlugin.defaultUniversalScript
|
|||||||
|
|
||||||
val scalacVersion = "2.12.10"
|
val scalacVersion = "2.12.10"
|
||||||
val graalVersion = "19.2.0.1"
|
val graalVersion = "19.2.0.1"
|
||||||
|
val circeVersion = "0.11.1"
|
||||||
organization in ThisBuild := "org.enso"
|
organization in ThisBuild := "org.enso"
|
||||||
scalaVersion in ThisBuild := scalacVersion
|
scalaVersion in ThisBuild := scalacVersion
|
||||||
|
|
||||||
@ -98,6 +99,7 @@ lazy val enso = (project in file("."))
|
|||||||
pkg,
|
pkg,
|
||||||
project_manager,
|
project_manager,
|
||||||
runtime,
|
runtime,
|
||||||
|
parser_service,
|
||||||
syntax,
|
syntax,
|
||||||
syntax_definition,
|
syntax_definition,
|
||||||
unused
|
unused
|
||||||
@ -131,8 +133,8 @@ val scala_compiler = Seq(
|
|||||||
"org.scala-lang" % "scala-compiler" % scalacVersion
|
"org.scala-lang" % "scala-compiler" % scalacVersion
|
||||||
)
|
)
|
||||||
|
|
||||||
val circe = Seq("circe-core", "circe-generic", "circe-yaml")
|
val circe = Seq("circe-core", "circe-generic", "circe-parser")
|
||||||
.map("io.circe" %% _ % "0.10.0")
|
.map("io.circe" %% _ % circeVersion)
|
||||||
|
|
||||||
def akkaPkg(name: String) = akkaURL %% s"akka-$name" % akkaVersion
|
def akkaPkg(name: String) = akkaURL %% s"akka-$name" % akkaVersion
|
||||||
def akkaHTTPPkg(name: String) = akkaURL %% s"akka-$name" % akkaHTTPVersion
|
def akkaHTTPPkg(name: String) = akkaURL %% s"akka-$name" % akkaHTTPVersion
|
||||||
@ -185,7 +187,7 @@ lazy val unused = (project in file("common/scala/unused"))
|
|||||||
lazy val syntax_definition = (project in file("common/scala/syntax/definition"))
|
lazy val syntax_definition = (project in file("common/scala/syntax/definition"))
|
||||||
.dependsOn(logger, flexer)
|
.dependsOn(logger, flexer)
|
||||||
.settings(
|
.settings(
|
||||||
libraryDependencies ++= monocle ++ cats ++ scala_compiler ++ Seq(
|
libraryDependencies ++= monocle ++ cats ++ circe ++ scala_compiler ++ Seq(
|
||||||
"com.lihaoyi" %% "scalatags" % "0.7.0"
|
"com.lihaoyi" %% "scalatags" % "0.7.0"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -230,7 +232,7 @@ lazy val syntax = (project in file("common/scala/syntax/specialization"))
|
|||||||
inConfig(Benchmark)(Defaults.testSettings),
|
inConfig(Benchmark)(Defaults.testSettings),
|
||||||
bench := (test in Benchmark).tag(Exclusive).value,
|
bench := (test in Benchmark).tag(Exclusive).value,
|
||||||
parallelExecution in Benchmark := false,
|
parallelExecution in Benchmark := false,
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= circe ++ Seq(
|
||||||
"com.storm-enroute" %% "scalameter" % "0.17" % "bench",
|
"com.storm-enroute" %% "scalameter" % "0.17" % "bench",
|
||||||
"org.scalatest" %% "scalatest" % "3.0.5" % Test,
|
"org.scalatest" %% "scalatest" % "3.0.5" % Test,
|
||||||
"com.lihaoyi" %% "pprint" % "0.5.3"
|
"com.lihaoyi" %% "pprint" % "0.5.3"
|
||||||
@ -249,11 +251,21 @@ lazy val syntax = (project in file("common/scala/syntax/specialization"))
|
|||||||
.value
|
.value
|
||||||
)
|
)
|
||||||
|
|
||||||
|
lazy val parser_service = (project in file("common/scala/parser-service"))
|
||||||
|
.dependsOn(syntax)
|
||||||
|
.settings(
|
||||||
|
libraryDependencies ++= akka,
|
||||||
|
mainClass := Some("org.enso.ParserServiceMain")
|
||||||
|
)
|
||||||
|
|
||||||
lazy val pkg = (project in file("common/scala/pkg"))
|
lazy val pkg = (project in file("common/scala/pkg"))
|
||||||
.settings(
|
.settings(
|
||||||
mainClass in (Compile, run) := Some("org.enso.pkg.Main"),
|
mainClass in (Compile, run) := Some("org.enso.pkg.Main"),
|
||||||
version := "0.1",
|
version := "0.1",
|
||||||
libraryDependencies ++= circe ++ Seq("commons-io" % "commons-io" % "2.6")
|
libraryDependencies ++= circe ++ Seq(
|
||||||
|
"io.circe" %% "circe-yaml" % "0.10.0", // separate from other circe deps because its independent project with its own versioning
|
||||||
|
"commons-io" % "commons-io" % "2.6"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
lazy val file_manager = (project in file("common/scala/file-manager"))
|
lazy val file_manager = (project in file("common/scala/file-manager"))
|
||||||
|
20
common/rust/parser/Cargo.toml
Normal file
20
common/rust/parser/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "parser"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Michal Wawrzyniec Urbanczyk <michal.urbanczyk@luna-lang.org>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
failure = "0.1"
|
||||||
|
matches = "0.1"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
shrinkwraprs = "0.2.1"
|
||||||
|
wasm-bindgen = "0.2"
|
||||||
|
wasm-bindgen-test = "0.2"
|
||||||
|
|
||||||
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
|
websocket = "0.23.0"
|
40
common/rust/parser/src/api.rs
Normal file
40
common/rust/parser/src/api.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use failure::Fail;
|
||||||
|
|
||||||
|
// ============
|
||||||
|
// == Parser ==
|
||||||
|
// ============
|
||||||
|
|
||||||
|
/// Entity being able to parse Luna programs into Luna's AST.
|
||||||
|
pub trait IsParser {
|
||||||
|
fn parse(&mut self, program: String) -> Result<AST>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========
|
||||||
|
// == AST ==
|
||||||
|
// =========
|
||||||
|
|
||||||
|
// TODO: placeholder until we have real AST, see:
|
||||||
|
// https://github.com/luna/enso/issues/296
|
||||||
|
pub type AST = String;
|
||||||
|
|
||||||
|
// ===========
|
||||||
|
// == Error ==
|
||||||
|
// ===========
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Debug, Fail)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Error due to inner workings of the parser.
|
||||||
|
#[fail(display = "Internal parser error: {:?}", _0)]
|
||||||
|
ParsingError(String),
|
||||||
|
/// Error related to wrapping = communication with the parser service.
|
||||||
|
#[fail(display = "Interop error: {}", _0)]
|
||||||
|
InteropError(#[cause] Box<dyn failure::Fail>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wraps an arbitrary `std::error::Error` as an `InteropError.`
|
||||||
|
pub fn interop_error<T>(error: T) -> Error
|
||||||
|
where T: Fail {
|
||||||
|
Error::InteropError(Box::new(error))
|
||||||
|
}
|
30
common/rust/parser/src/jsclient.rs
Normal file
30
common/rust/parser/src/jsclient.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use crate::{api, api::IsParser};
|
||||||
|
use failure::Fail;
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Debug, Fail)]
|
||||||
|
pub enum Error {
|
||||||
|
#[fail(display = "JS parser client has not been yet implemented!")]
|
||||||
|
NotImplemented,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper over the JS-compiled parser.
|
||||||
|
///
|
||||||
|
/// Can only be used when targeting WebAssembly. Not yet implemented.
|
||||||
|
pub struct Client {}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
// avoid warnings when compiling natively and having this usage cfg-ed out
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn new() -> Result<Client> {
|
||||||
|
Err(Error::NotImplemented)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IsParser for Client {
|
||||||
|
fn parse(&mut self, _program: String) -> api::Result<api::AST> {
|
||||||
|
Err(api::interop_error(Error::NotImplemented))
|
||||||
|
}
|
||||||
|
}
|
45
common/rust/parser/src/lib.rs
Normal file
45
common/rust/parser/src/lib.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
pub mod api;
|
||||||
|
|
||||||
|
mod jsclient;
|
||||||
|
mod wsclient;
|
||||||
|
|
||||||
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
|
/// Handle to a parser implementation.
|
||||||
|
///
|
||||||
|
/// Currently this component is implemented as a wrapper over parser written
|
||||||
|
/// in Scala. Depending on compilation target (native or wasm) it uses either
|
||||||
|
/// implementation provided by `wsclient` or `jsclient`.
|
||||||
|
#[derive(shrinkwraprs::Shrinkwrap)]
|
||||||
|
#[shrinkwrap(mutable)]
|
||||||
|
pub struct Parser(pub Box<dyn api::IsParser>);
|
||||||
|
|
||||||
|
impl Parser {
|
||||||
|
/// Obtains a default parser implementation.
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
pub fn new() -> api::Result<Parser> {
|
||||||
|
let client = wsclient::Client::new()?;
|
||||||
|
let parser = Box::new(client);
|
||||||
|
Ok(Parser(parser))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtains a default parser implementation.
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
pub fn new() -> api::Result<ParserWrapper> {
|
||||||
|
let client = jsclient::Client::new()?;
|
||||||
|
let parser = Box::new(client);
|
||||||
|
Ok(parser)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtains a default parser implementation, panicking in case of failure.
|
||||||
|
pub fn new_or_panic() -> Parser {
|
||||||
|
Parser::new()
|
||||||
|
.unwrap_or_else(|e| panic!("Failed to create a parser: {:?}", e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl api::IsParser for Parser {
|
||||||
|
fn parse(&mut self, program: String) -> api::Result<api::AST> {
|
||||||
|
self.deref_mut().parse(program)
|
||||||
|
}
|
||||||
|
}
|
16
common/rust/parser/src/main.rs
Normal file
16
common/rust/parser/src/main.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
use parser::api::IsParser;
|
||||||
|
|
||||||
|
/// Simple interactive tester - calls parser with its argument (or a
|
||||||
|
/// hardcoded default) and prints the result
|
||||||
|
fn main() {
|
||||||
|
let default_input = String::from("import Foo.Bar\nfoo = a + 2");
|
||||||
|
let program = std::env::args().nth(1).unwrap_or(default_input);
|
||||||
|
println!("Will parse: {}", program);
|
||||||
|
|
||||||
|
let mut parser = parser::Parser::new_or_panic();
|
||||||
|
let output = parser.parse(program);
|
||||||
|
match output {
|
||||||
|
Ok(result) => println!("Parser responded with: {:?}", result),
|
||||||
|
Err(e) => println!("Failed to obtain a response: {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
208
common/rust/parser/src/wsclient.rs
Normal file
208
common/rust/parser/src/wsclient.rs
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
#![cfg(not(target_arch = "wasm32"))]
|
||||||
|
|
||||||
|
use failure::Fail;
|
||||||
|
use std::default::Default;
|
||||||
|
use websocket::{
|
||||||
|
stream::sync::TcpStream, ClientBuilder, Message, OwnedMessage,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::api;
|
||||||
|
use api::Error::*;
|
||||||
|
use Error::*;
|
||||||
|
|
||||||
|
type WsTcpClient = websocket::sync::Client<TcpStream>;
|
||||||
|
|
||||||
|
// ==========================
|
||||||
|
// == Constants & literals ==
|
||||||
|
// ==========================
|
||||||
|
|
||||||
|
pub const LOCALHOST: &str = "localhost";
|
||||||
|
pub const DEFAULT_PORT: i32 = 30615;
|
||||||
|
pub const DEFAULT_HOSTNAME: &str = LOCALHOST;
|
||||||
|
|
||||||
|
pub const HOSTNAME_VAR: &str = "ENSO_PARSER_HOSTNAME";
|
||||||
|
pub const PORT_VAR: &str = "ENSO_PARSER_PORT";
|
||||||
|
|
||||||
|
// ===========
|
||||||
|
// == Error ==
|
||||||
|
// ===========
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Debug, Fail)]
|
||||||
|
pub enum Error {
|
||||||
|
#[fail(display = "Failed to parse given address url: {}", _0)]
|
||||||
|
WrongUrl(#[cause] websocket::client::ParseError),
|
||||||
|
|
||||||
|
#[fail(display = "Connection error: {}", _0)]
|
||||||
|
ConnectivityError(#[cause] websocket::WebSocketError),
|
||||||
|
|
||||||
|
#[fail(display = "Peer has closed the connection")]
|
||||||
|
PeerClosedConnection,
|
||||||
|
|
||||||
|
#[fail(display = "Received non-text response: {:?}", _0)]
|
||||||
|
NonTextResponse(websocket::OwnedMessage),
|
||||||
|
|
||||||
|
#[fail(display = "JSON (de)serialization failed: {:?}", _0)]
|
||||||
|
JsonSerializationError(#[cause] serde_json::error::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Error> for api::Error {
|
||||||
|
fn from(e: Error) -> Self {
|
||||||
|
api::interop_error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<websocket::client::ParseError> for Error {
|
||||||
|
fn from(error: websocket::client::ParseError) -> Self {
|
||||||
|
WrongUrl(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<websocket::WebSocketError> for Error {
|
||||||
|
fn from(error: websocket::WebSocketError) -> Self {
|
||||||
|
ConnectivityError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<serde_json::error::Error> for Error {
|
||||||
|
fn from(error: serde_json::error::Error) -> Self {
|
||||||
|
JsonSerializationError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==============
|
||||||
|
// == Protocol ==
|
||||||
|
// ==============
|
||||||
|
|
||||||
|
/// All request supported by the Parser Service.
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub enum Request {
|
||||||
|
ParseRequest { program: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
/// All responses that Parser Service might reply with.
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub enum Response {
|
||||||
|
Success { ast: String },
|
||||||
|
Error { message: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============
|
||||||
|
// == Config ==
|
||||||
|
// ============
|
||||||
|
|
||||||
|
/// Describes a WS endpoint.
|
||||||
|
pub struct Config {
|
||||||
|
pub host: String,
|
||||||
|
pub port: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
/// Formats URL String describing a WS endpoint.
|
||||||
|
pub fn address_string(&self) -> String {
|
||||||
|
format!("ws://{}:{}", self.host, self.port)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtains a default WS endpoint to use to connect to parser service
|
||||||
|
/// using environment variables or, if they are not set, hardcoded
|
||||||
|
/// defaults.
|
||||||
|
pub fn from_env() -> Config {
|
||||||
|
let host = env_var_or(HOSTNAME_VAR, DEFAULT_HOSTNAME);
|
||||||
|
let port = env_var_or(PORT_VAR, Default::default())
|
||||||
|
.parse()
|
||||||
|
.unwrap_or(DEFAULT_PORT);
|
||||||
|
Config { host, port }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn env_var_or(varname: &str, default_value: &str) -> String {
|
||||||
|
std::env::var(varname).unwrap_or_else(|_| default_value.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============
|
||||||
|
// == Client ==
|
||||||
|
// ============
|
||||||
|
|
||||||
|
/// Client to the Parser Service written in Scala.
|
||||||
|
///
|
||||||
|
/// Connects through WebSocket to the running service.
|
||||||
|
pub struct Client {
|
||||||
|
connection: WsTcpClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
mod internal {
|
||||||
|
use super::*;
|
||||||
|
impl Client {
|
||||||
|
/// Serializes `Request` to JSON and sends to peer as a text message.
|
||||||
|
pub fn send_request(&mut self, request: Request) -> Result<()> {
|
||||||
|
let request_txt = serde_json::to_string(&request)?;
|
||||||
|
let message = Message::text(request_txt);
|
||||||
|
self.connection.send_message(&message)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtains a text message from peer and deserializes it using JSON
|
||||||
|
/// into a `Response`.
|
||||||
|
///
|
||||||
|
/// Should be called exactly once after each `send_request` invocation.
|
||||||
|
pub fn recv_response(&mut self) -> Result<Response> {
|
||||||
|
let response = self.connection.recv_message()?;
|
||||||
|
match response {
|
||||||
|
OwnedMessage::Text(text) => Ok(serde_json::from_str(&text)?),
|
||||||
|
_ => Err(NonTextResponse(response)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends given `Request` to peer and receives a `Response`.
|
||||||
|
///
|
||||||
|
/// Both request and response are exchanged in JSON using text messages
|
||||||
|
/// over WebSocket.
|
||||||
|
pub fn rpc_call(&mut self, request: Request) -> Result<Response> {
|
||||||
|
self.send_request(request)?;
|
||||||
|
self.recv_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
/// Creates a new `Client` connected to the already running parser service.
|
||||||
|
pub fn from_conf(config: &Config) -> Result<Client> {
|
||||||
|
let address = config.address_string();
|
||||||
|
let mut builder = ClientBuilder::new(&address)?;
|
||||||
|
let connection = builder.connect_insecure()?;
|
||||||
|
Ok(Client { connection })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a `Client` using configuration defined by environment or
|
||||||
|
/// defaults if environment is not set.
|
||||||
|
pub fn new() -> Result<Client> {
|
||||||
|
let config = Config::from_env();
|
||||||
|
println!("Connecting to {}", config.address_string());
|
||||||
|
let client = Client::from_conf(&config)?;
|
||||||
|
println!("Established connection with {}", config.address_string());
|
||||||
|
Ok(client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl api::IsParser for Client {
|
||||||
|
fn parse(&mut self, program: String) -> api::Result<api::AST> {
|
||||||
|
let request = Request::ParseRequest { program };
|
||||||
|
let response = self.rpc_call(request)?;
|
||||||
|
match response {
|
||||||
|
Response::Success { ast } => Ok(ast),
|
||||||
|
Response::Error { message } => Err(ParsingError(message)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========
|
||||||
|
// == tests ==
|
||||||
|
// ===========
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wrong_url_reported() {
|
||||||
|
let invalid_hostname = String::from("bgjhkb 7");
|
||||||
|
let wrong_config = Config { host: invalid_hostname, port: 8080 };
|
||||||
|
let client = Client::from_conf(&wrong_config);
|
||||||
|
let got_wrong_url_error = matches::matches!(client, Err(WrongUrl(_)));
|
||||||
|
assert!(got_wrong_url_error, "expected WrongUrl error");
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package org.enso
|
||||||
|
|
||||||
|
import io.circe.syntax._
|
||||||
|
import io.circe.generic.auto._
|
||||||
|
import org.enso.flexer.Reader
|
||||||
|
import org.enso.parserservice.Protocol
|
||||||
|
import org.enso.parserservice.Server
|
||||||
|
import org.enso.syntax.text.AST
|
||||||
|
import org.enso.syntax.text.Parser
|
||||||
|
|
||||||
|
import scala.util.Try
|
||||||
|
|
||||||
|
object ParserService {
|
||||||
|
val HOSTNAME_VAR = "ENSO_PARSER_HOSTNAME"
|
||||||
|
val PORT_VAR = "ENSO_PARSER_PORT"
|
||||||
|
|
||||||
|
val DEFAULT_PORT = 30615
|
||||||
|
val DEFAULT_HOSTNAME = "localhost"
|
||||||
|
|
||||||
|
/** Obtains configuration from environment, filling missing values with
|
||||||
|
* defaults.
|
||||||
|
*/
|
||||||
|
def configFromEnv(): Server.Config = {
|
||||||
|
val hostname = sys.env.getOrElse(HOSTNAME_VAR, DEFAULT_HOSTNAME)
|
||||||
|
val port = sys.env
|
||||||
|
.get(PORT_VAR)
|
||||||
|
.flatMap(str => Try { str.toInt }.toOption)
|
||||||
|
.getOrElse(DEFAULT_PORT)
|
||||||
|
Server.Config(hostname, port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Class that allows setting up parser service with given configuration. */
|
||||||
|
case class ParserService() extends Server with Protocol {
|
||||||
|
import parserservice._
|
||||||
|
import Protocol._
|
||||||
|
|
||||||
|
def serializeAst(ast: AST.Module): String =
|
||||||
|
ast.asJson.noSpaces
|
||||||
|
|
||||||
|
def runParser(program: String): AST =
|
||||||
|
new Parser().run(new Reader(program))
|
||||||
|
|
||||||
|
def handleRequest(request: Request): Response = {
|
||||||
|
request match {
|
||||||
|
case ParseRequest(program) =>
|
||||||
|
val ast = runParser(program)
|
||||||
|
Protocol.Success(ast.asJson.toString())
|
||||||
|
case _ =>
|
||||||
|
throw new Exception(f"unimplemented request: $request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Runs a simple WebSocket server that wraps Parser into a service. */
|
||||||
|
object ParserServiceMain extends App {
|
||||||
|
import ParserService._
|
||||||
|
println("Getting configuration from environment...")
|
||||||
|
val config = configFromEnv()
|
||||||
|
|
||||||
|
println(s"Will serve ${config.addressString()}")
|
||||||
|
println(
|
||||||
|
s"To change configuration, restart with $HOSTNAME_VAR or " +
|
||||||
|
s"$PORT_VAR variables set to desired values"
|
||||||
|
)
|
||||||
|
val service = ParserService()
|
||||||
|
service.start(config)
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package org.enso.parserservice
|
||||||
|
|
||||||
|
import io.circe.parser._
|
||||||
|
import io.circe.generic.auto._
|
||||||
|
import io.circe.syntax._
|
||||||
|
|
||||||
|
/** Types implementing parser server protocol.
|
||||||
|
*
|
||||||
|
* The protocol always is single request -> single response.
|
||||||
|
*/
|
||||||
|
object Protocol {
|
||||||
|
sealed trait Request
|
||||||
|
final case class ParseRequest(program: String) extends Request
|
||||||
|
|
||||||
|
sealed trait Response
|
||||||
|
final case class Success(ast: String) extends Response
|
||||||
|
final case class Error(message: String) extends Response
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Helper for implementing protocol over text-based transport.
|
||||||
|
*
|
||||||
|
* Requests and responses are marshaled as text using JSON
|
||||||
|
* (and default circe serialzation schema).
|
||||||
|
*/
|
||||||
|
trait Protocol {
|
||||||
|
import Protocol._
|
||||||
|
|
||||||
|
/** Generate [[Response]] for a given [[Request]].
|
||||||
|
*
|
||||||
|
* Any [[Throwable]] thrown out of implementation will be translated into
|
||||||
|
* [[Protocol.Error]] message.
|
||||||
|
*/
|
||||||
|
def handleRequest(request: Request): Response
|
||||||
|
|
||||||
|
def handleMessage(input: String): String = {
|
||||||
|
try {
|
||||||
|
decode[Request](input) match {
|
||||||
|
case Left(err) => throw err
|
||||||
|
case Right(request) => handleRequest(request).asJson.toString()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
case e: Throwable => (Error(e.toString): Response).asJson.noSpaces
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
package org.enso.parserservice
|
||||||
|
|
||||||
|
import akka.NotUsed
|
||||||
|
import akka.actor.ActorSystem
|
||||||
|
import akka.http.scaladsl.Http
|
||||||
|
import akka.http.scaladsl.model.HttpMethods.GET
|
||||||
|
import akka.http.scaladsl.model.HttpRequest
|
||||||
|
import akka.http.scaladsl.model.HttpResponse
|
||||||
|
import akka.http.scaladsl.model.StatusCodes
|
||||||
|
import akka.http.scaladsl.model.ws.BinaryMessage
|
||||||
|
import akka.http.scaladsl.model.ws.Message
|
||||||
|
import akka.http.scaladsl.model.ws.TextMessage
|
||||||
|
import akka.http.scaladsl.model.ws.UpgradeToWebSocket
|
||||||
|
import akka.stream.ActorMaterializer
|
||||||
|
import akka.stream.scaladsl.Flow
|
||||||
|
import akka.stream.scaladsl.Sink
|
||||||
|
import akka.stream.scaladsl.Source
|
||||||
|
|
||||||
|
import scala.concurrent.ExecutionContext
|
||||||
|
import scala.util.Failure
|
||||||
|
import scala.util.Success
|
||||||
|
|
||||||
|
object Server {
|
||||||
|
|
||||||
|
/** Describes endpoint to which [[Server]] can bind. */
|
||||||
|
final case class Config(interface: String, port: Int) {
|
||||||
|
def addressString(): String = s"ws://$interface:$port"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** WebSocket server supporting synchronous request-response protocol.
|
||||||
|
*
|
||||||
|
* Server when run binds to endpoint and accepts establishing web socket
|
||||||
|
* connection for any number of peers.
|
||||||
|
*
|
||||||
|
* Server replies to each incoming text message with a single text message.
|
||||||
|
* Server accepts a single Text Message from a peer and responds with
|
||||||
|
* another Text Message.
|
||||||
|
*/
|
||||||
|
trait Server {
|
||||||
|
implicit val system: ActorSystem = ActorSystem()
|
||||||
|
implicit val materializer: ActorMaterializer = ActorMaterializer()
|
||||||
|
|
||||||
|
/** Generate text reply for given request text message. */
|
||||||
|
def handleMessage(input: String): String
|
||||||
|
|
||||||
|
/** Akka stream defining server behavior.
|
||||||
|
*
|
||||||
|
* Incoming [[TextMessage]]s are replied to (see [[handleMessage]]).
|
||||||
|
* Incoming binary messages are ignored.
|
||||||
|
*/
|
||||||
|
val handlerFlow: Flow[Message, TextMessage.Strict, NotUsed] =
|
||||||
|
Flow[Message]
|
||||||
|
.flatMapConcat {
|
||||||
|
case tm: TextMessage =>
|
||||||
|
val strict = tm.textStream.fold("")(_ + _)
|
||||||
|
strict.map(input => TextMessage(handleMessage(input)))
|
||||||
|
case bm: BinaryMessage =>
|
||||||
|
bm.dataStream.runWith(Sink.ignore)
|
||||||
|
Source.empty
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Server behavior upon receiving HTTP request.
|
||||||
|
*
|
||||||
|
* As server implements websocket-based protocol, this implementation accepts
|
||||||
|
* only GET requests to set up WebSocket connection.
|
||||||
|
*
|
||||||
|
* The request's URI is not checked.
|
||||||
|
*/
|
||||||
|
val handleRequest: HttpRequest => HttpResponse = {
|
||||||
|
case req @ HttpRequest(GET, _, _, _, _) =>
|
||||||
|
req.header[UpgradeToWebSocket] match {
|
||||||
|
case Some(upgrade) =>
|
||||||
|
println("Establishing a new connection")
|
||||||
|
upgrade.handleMessages(handlerFlow)
|
||||||
|
case None =>
|
||||||
|
HttpResponse(
|
||||||
|
StatusCodes.BadRequest,
|
||||||
|
entity = "Not a valid websocket request!"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case r: HttpRequest =>
|
||||||
|
r.discardEntityBytes()
|
||||||
|
HttpResponse(StatusCodes.MethodNotAllowed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Starts a HTTP server listening at the given endpoint.
|
||||||
|
*
|
||||||
|
* Function is asynchronous, will return immediately. If the server fails to
|
||||||
|
* start, function will exit the process with a non-zero code.
|
||||||
|
*/
|
||||||
|
def start(config: Server.Config): Unit = {
|
||||||
|
val bindingFuture =
|
||||||
|
Http().bindAndHandleSync(
|
||||||
|
handleRequest,
|
||||||
|
interface = config.interface,
|
||||||
|
port = config.port
|
||||||
|
)
|
||||||
|
|
||||||
|
bindingFuture.onComplete({
|
||||||
|
case Success(_) =>
|
||||||
|
println(s"Server online at ${config.addressString()}")
|
||||||
|
case Failure(exception) =>
|
||||||
|
println(s"Failed to start server: $exception")
|
||||||
|
system.terminate()
|
||||||
|
System.exit(1)
|
||||||
|
})(ExecutionContext.global)
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,9 @@
|
|||||||
package org.enso.data
|
package org.enso.data
|
||||||
|
|
||||||
case class Tree[K, V](value: Option[V], branches: Map[K, Tree[K, V]]) {
|
import io.circe.Decoder
|
||||||
|
import io.circe.Encoder
|
||||||
|
|
||||||
|
final case class Tree[K, V](value: Option[V], branches: Map[K, Tree[K, V]]) {
|
||||||
def +(item: (List[K], V)): Tree[K, V] = item._1 match {
|
def +(item: (List[K], V)): Tree[K, V] = item._1 match {
|
||||||
case Nil => this.copy(value = Some(item._2))
|
case Nil => this.copy(value = Some(item._2))
|
||||||
case p :: ps => {
|
case p :: ps => {
|
||||||
@ -34,4 +37,27 @@ object Tree {
|
|||||||
def apply[K, V](): Tree[K, V] = new Tree(None, Map())
|
def apply[K, V](): Tree[K, V] = new Tree(None, Map())
|
||||||
def apply[K, V](deps: (List[K], V)*): Tree[K, V] =
|
def apply[K, V](deps: (List[K], V)*): Tree[K, V] =
|
||||||
deps.foldLeft(Tree[K, V]())(_ + _)
|
deps.foldLeft(Tree[K, V]())(_ + _)
|
||||||
|
|
||||||
|
//////////////////
|
||||||
|
// JSON support //
|
||||||
|
//////////////////
|
||||||
|
|
||||||
|
/* Note [Tree Serialization] */
|
||||||
|
implicit def jsonEncode[K: Encoder, V: Encoder]: Encoder[Tree[K, V]] =
|
||||||
|
Encoder.forProduct2("value", "branches")(
|
||||||
|
tree => tree.value -> tree.branches.toSeq
|
||||||
|
)
|
||||||
|
|
||||||
|
/* Note [Tree Serialization]
|
||||||
|
* We can't directly serialize Map[K,V], as circe tries to use whole K as a
|
||||||
|
* key string in the generated JSON. Thus, we serialize Map[K, V] by
|
||||||
|
* converting it to Seq[(K,V)] first.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Note [Tree Serialization] */
|
||||||
|
implicit def jsonDecode[K: Decoder, V: Decoder]: Decoder[Tree[K, V]] =
|
||||||
|
Decoder.forProduct2("value", "branches")(
|
||||||
|
(value: Option[V], branches: Seq[(K, Tree[K, V])]) =>
|
||||||
|
Tree(value, branches.toMap)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,14 @@ package org.enso.syntax.text
|
|||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
import shapeless.Id
|
|
||||||
import cats.Foldable
|
import cats.Foldable
|
||||||
import cats.Functor
|
import cats.Functor
|
||||||
import cats.derived._
|
import cats.derived._
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
|
import io.circe.Encoder
|
||||||
|
import io.circe.Json
|
||||||
|
import io.circe.generic.AutoDerivation
|
||||||
|
import io.circe.generic.auto._
|
||||||
import org.enso.data.List1._
|
import org.enso.data.List1._
|
||||||
import org.enso.data.Index
|
import org.enso.data.Index
|
||||||
import org.enso.data.List1
|
import org.enso.data.List1
|
||||||
@ -266,53 +269,27 @@ object AST {
|
|||||||
case a: ASTOf[_] => shape == a.shape
|
case a: ASTOf[_] => shape == a.shape
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
val repr: Repr.Builder = cls.repr(shape)
|
val repr: Repr.Builder = cls.repr(shape)
|
||||||
val span: Int = cls.repr(shape).span
|
val span: Int = cls.repr(shape).span
|
||||||
def show(): String = repr.build()
|
def show(): String = repr.build()
|
||||||
def setID(newID: ID): ASTOf[T] = copy(id = Some(newID))
|
def setID(newID: ID): ASTOf[T] = copy(id = Some(newID))
|
||||||
def withNewID(): ASTOf[T] = copy(id = Some(UUID.randomUUID()))
|
def withNewID(): ASTOf[T] = copy(id = Some(UUID.randomUUID()))
|
||||||
def map(f: AST => AST): ASTOf[T] = copy(shape = cls.map(shape)(f))
|
def map(f: AST => AST): ASTOf[T] = copy(shape = cls.map(shape)(f))
|
||||||
def mapWithOff(f: (Index, AST) => AST): ASTOf[T] =
|
def mapWithOff(f: (Index, AST) => AST): ASTOf[T] =
|
||||||
copy(shape = cls.mapWithOff(shape)(f))
|
copy(shape = cls.mapWithOff(shape)(f))
|
||||||
def zipWithOffset(): T[(Index, AST)] = cls.zipWithOffset(shape)
|
def zipWithOffset(): T[(Index, AST)] = cls.zipWithOffset(shape)
|
||||||
|
def encodeShape(): Json = cls.encode(shape)
|
||||||
}
|
}
|
||||||
object ASTOf {
|
object ASTOf extends AutoDerivation {
|
||||||
implicit def repr[T[_]]: Repr[ASTOf[T]] = _.repr
|
implicit def repr[T[_]]: Repr[ASTOf[T]] = _.repr
|
||||||
implicit def unwrap[T[_]](t: ASTOf[T]): T[AST] = t.shape
|
implicit def unwrap[T[_]](t: ASTOf[T]): T[AST] = t.shape
|
||||||
implicit def wrap[T[_]](t: T[AST])(
|
implicit def wrap[T[_]](t: T[AST])(
|
||||||
implicit
|
implicit
|
||||||
ev: ASTClass[T]
|
ev: ASTClass[T]
|
||||||
): ASTOf[T] = ASTOf(t)
|
): ASTOf[T] = ASTOf(t)
|
||||||
}
|
|
||||||
|
|
||||||
//// ASTClass ////
|
implicit def jsonEncoder[T[_]]: Encoder[ASTOf[T]] =
|
||||||
|
Encoder.forProduct2("shape", "id")(ast => ast.encodeShape() -> ast.id)
|
||||||
/** [[ASTClass]] implements set of AST operations based on a precise AST
|
|
||||||
* shape. Because the [[T]] parameter in [[ASTOf]] is covariant, we may lose
|
|
||||||
* information about the shape after we construct the AST, thus this instance
|
|
||||||
* is used to cache all necessary operations during AST construction.
|
|
||||||
*/
|
|
||||||
trait ASTClass[T[_]] {
|
|
||||||
def repr(t: T[AST]): Repr.Builder
|
|
||||||
def map(t: T[AST])(f: AST => AST): T[AST]
|
|
||||||
def mapWithOff(t: T[AST])(f: (Index, AST) => AST): T[AST]
|
|
||||||
def zipWithOffset(t: T[AST]): T[(Index, AST)]
|
|
||||||
}
|
|
||||||
object ASTClass {
|
|
||||||
def apply[T[_]](implicit cls: ASTClass[T]): ASTClass[T] = cls
|
|
||||||
implicit def instance[T[_]](
|
|
||||||
implicit
|
|
||||||
evRepr: Repr[T[AST]],
|
|
||||||
evFtor: Functor[T],
|
|
||||||
evOzip: OffsetZip[T, AST]
|
|
||||||
): ASTClass[T] =
|
|
||||||
new ASTClass[T] {
|
|
||||||
def repr(t: T[AST]): Repr.Builder = evRepr.repr(t)
|
|
||||||
def map(t: T[AST])(f: AST => AST): T[AST] = Functor[T].map(t)(f)
|
|
||||||
def zipWithOffset(t: T[AST]): T[(Index, AST)] = OffsetZip(t)
|
|
||||||
def mapWithOff(t: T[AST])(f: (Index, AST) => AST): T[AST] =
|
|
||||||
Functor[T].map(zipWithOffset(t))(f.tupled)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//// ASTOps ////
|
//// ASTOps ////
|
||||||
@ -352,7 +329,7 @@ object AST {
|
|||||||
* a type is phantom, then its last type argument is not used and we can
|
* a type is phantom, then its last type argument is not used and we can
|
||||||
* safely coerce it to something else.
|
* safely coerce it to something else.
|
||||||
*/
|
*/
|
||||||
trait Phantom
|
sealed trait Phantom
|
||||||
implicit class PhantomOps[T[_] <: Phantom](ident: T[_]) {
|
implicit class PhantomOps[T[_] <: Phantom](ident: T[_]) {
|
||||||
def coerce[S]: T[S] = ident.asInstanceOf[T[S]]
|
def coerce[S]: T[S] = ident.asInstanceOf[T[S]]
|
||||||
}
|
}
|
||||||
@ -380,8 +357,8 @@ object AST {
|
|||||||
val any = UnapplyByType[Invalid]
|
val any = UnapplyByType[Invalid]
|
||||||
|
|
||||||
object Unrecognized {
|
object Unrecognized {
|
||||||
val any = UnapplyByType[Unrecognized]
|
val any = UnapplyByType[Unrecognized]
|
||||||
def unapply(t: AST) = Unapply[Unrecognized].run(_.str)(t)
|
def unapply(t: AST) = Unapply[Unrecognized].run(_.str)(t)
|
||||||
def apply(str: String): Unrecognized = UnrecognizedOf[AST](str)
|
def apply(str: String): Unrecognized = UnrecognizedOf[AST](str)
|
||||||
}
|
}
|
||||||
object Unexpected {
|
object Unexpected {
|
||||||
@ -394,15 +371,15 @@ object AST {
|
|||||||
//// Instances ////
|
//// Instances ////
|
||||||
|
|
||||||
object UnrecognizedOf {
|
object UnrecognizedOf {
|
||||||
implicit def ftor: Functor[UnrecognizedOf] = semi.functor
|
implicit def ftor: Functor[UnrecognizedOf] = semi.functor
|
||||||
implicit def fold: Foldable[UnrecognizedOf] = semi.foldable
|
implicit def fold: Foldable[UnrecognizedOf] = semi.foldable
|
||||||
implicit def repr[T]: Repr[UnrecognizedOf[T]] = _.str
|
implicit def repr[T]: Repr[UnrecognizedOf[T]] = _.str
|
||||||
implicit def ozip[T]: OffsetZip[UnrecognizedOf, T] = t => t.coerce
|
implicit def ozip[T]: OffsetZip[UnrecognizedOf, T] = t => t.coerce
|
||||||
}
|
}
|
||||||
object UnexpectedOf {
|
object UnexpectedOf {
|
||||||
implicit def ftor: Functor[UnexpectedOf] = semi.functor
|
implicit def ftor: Functor[UnexpectedOf] = semi.functor
|
||||||
implicit def fold: Foldable[UnexpectedOf] = semi.foldable
|
implicit def fold: Foldable[UnexpectedOf] = semi.foldable
|
||||||
implicit def repr[T: Repr]: Repr[UnexpectedOf[T]] = t => Repr(t.stream)
|
implicit def repr[T: Repr]: Repr[UnexpectedOf[T]] = t => Repr(t.stream)
|
||||||
implicit def ozip[T: Repr]: OffsetZip[UnexpectedOf, T] =
|
implicit def ozip[T: Repr]: OffsetZip[UnexpectedOf, T] =
|
||||||
t => t.copy(stream = OffsetZip(t.stream))
|
t => t.copy(stream = OffsetZip(t.stream))
|
||||||
}
|
}
|
||||||
@ -452,32 +429,32 @@ object AST {
|
|||||||
//// Instances ////
|
//// Instances ////
|
||||||
|
|
||||||
object BlankOf {
|
object BlankOf {
|
||||||
implicit def ftor: Functor[BlankOf] = semi.functor
|
implicit def ftor: Functor[BlankOf] = semi.functor
|
||||||
implicit def fold: Foldable[BlankOf] = semi.foldable
|
implicit def fold: Foldable[BlankOf] = semi.foldable
|
||||||
implicit def repr[T]: Repr[BlankOf[T]] = _.name
|
implicit def repr[T]: Repr[BlankOf[T]] = _.name
|
||||||
implicit def ozip[T]: OffsetZip[BlankOf, T] = t => t.coerce
|
implicit def ozip[T]: OffsetZip[BlankOf, T] = t => t.coerce
|
||||||
}
|
}
|
||||||
object VarOf {
|
object VarOf {
|
||||||
implicit def ftor: Functor[VarOf] = semi.functor
|
implicit def ftor: Functor[VarOf] = semi.functor
|
||||||
implicit def fold: Foldable[VarOf] = semi.foldable
|
implicit def fold: Foldable[VarOf] = semi.foldable
|
||||||
implicit def repr[T]: Repr[VarOf[T]] = _.name
|
implicit def repr[T]: Repr[VarOf[T]] = _.name
|
||||||
implicit def ozip[T]: OffsetZip[VarOf, T] = t => t.coerce
|
implicit def ozip[T]: OffsetZip[VarOf, T] = t => t.coerce
|
||||||
}
|
}
|
||||||
object ConsOf {
|
object ConsOf {
|
||||||
implicit def ftor: Functor[ConsOf] = semi.functor
|
implicit def ftor: Functor[ConsOf] = semi.functor
|
||||||
implicit def fold: Foldable[ConsOf] = semi.foldable
|
implicit def fold: Foldable[ConsOf] = semi.foldable
|
||||||
implicit def repr[T]: Repr[ConsOf[T]] = _.name
|
implicit def repr[T]: Repr[ConsOf[T]] = _.name
|
||||||
implicit def ozip[T]: OffsetZip[ConsOf, T] = t => t.coerce
|
implicit def ozip[T]: OffsetZip[ConsOf, T] = t => t.coerce
|
||||||
}
|
}
|
||||||
object OprOf {
|
object OprOf {
|
||||||
implicit def ftor: Functor[OprOf] = semi.functor
|
implicit def ftor: Functor[OprOf] = semi.functor
|
||||||
implicit def fold: Foldable[OprOf] = semi.foldable
|
implicit def fold: Foldable[OprOf] = semi.foldable
|
||||||
implicit def repr[T]: Repr[OprOf[T]] = _.name
|
implicit def repr[T]: Repr[OprOf[T]] = _.name
|
||||||
implicit def ozip[T]: OffsetZip[OprOf, T] = t => t.coerce
|
implicit def ozip[T]: OffsetZip[OprOf, T] = t => t.coerce
|
||||||
}
|
}
|
||||||
object ModOf {
|
object ModOf {
|
||||||
implicit def ftor: Functor[ModOf] = semi.functor
|
implicit def ftor: Functor[ModOf] = semi.functor
|
||||||
implicit def fold: Foldable[ModOf] = semi.foldable
|
implicit def fold: Foldable[ModOf] = semi.foldable
|
||||||
implicit def repr[T]: Repr[ModOf[T]] = R + _.name + "="
|
implicit def repr[T]: Repr[ModOf[T]] = R + _.name + "="
|
||||||
implicit def ozip[T]: OffsetZip[ModOf, T] = t => t.coerce
|
implicit def ozip[T]: OffsetZip[ModOf, T] = t => t.coerce
|
||||||
}
|
}
|
||||||
@ -485,10 +462,10 @@ object AST {
|
|||||||
//// Conversions ////
|
//// Conversions ////
|
||||||
|
|
||||||
trait Conversions1 {
|
trait Conversions1 {
|
||||||
implicit def strToVar(str: String): Var = Var(str)
|
implicit def strToVar(str: String): Var = Var(str)
|
||||||
implicit def strToCons(str: String): Cons = Cons(str)
|
implicit def strToCons(str: String): Cons = Cons(str)
|
||||||
implicit def strToOpr(str: String): Opr = Opr(str)
|
implicit def strToOpr(str: String): Opr = Opr(str)
|
||||||
implicit def strToMod(str: String): Mod = Mod(str)
|
implicit def strToMod(str: String): Mod = Mod(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
trait conversions extends Conversions1 {
|
trait conversions extends Conversions1 {
|
||||||
@ -509,31 +486,31 @@ object AST {
|
|||||||
private val blank = BlankOf[AST]()
|
private val blank = BlankOf[AST]()
|
||||||
val any = UnapplyByType[Blank]
|
val any = UnapplyByType[Blank]
|
||||||
def unapply(t: AST) = Unapply[Blank].run(_ => true)(t)
|
def unapply(t: AST) = Unapply[Blank].run(_ => true)(t)
|
||||||
def apply(): Blank = blank
|
def apply(): Blank = blank
|
||||||
}
|
}
|
||||||
object Var {
|
object Var {
|
||||||
private val pool = new Pool[VarOf[AST]]()
|
private val pool = new Pool[VarOf[AST]]()
|
||||||
val any = UnapplyByType[Var]
|
val any = UnapplyByType[Var]
|
||||||
def unapply(t: AST) = Unapply[Var].run(_.name)(t)
|
def unapply(t: AST) = Unapply[Var].run(_.name)(t)
|
||||||
def apply(name: String): Var = pool.get(VarOf[AST](name))
|
def apply(name: String): Var = pool.get(VarOf[AST](name))
|
||||||
}
|
}
|
||||||
object Cons {
|
object Cons {
|
||||||
private val pool = new Pool[ConsOf[AST]]()
|
private val pool = new Pool[ConsOf[AST]]()
|
||||||
val any = UnapplyByType[Cons]
|
val any = UnapplyByType[Cons]
|
||||||
def unapply(t: AST) = Unapply[Cons].run(_.name)(t)
|
def unapply(t: AST) = Unapply[Cons].run(_.name)(t)
|
||||||
def apply(name: String): Cons = pool.get(ConsOf[AST](name))
|
def apply(name: String): Cons = pool.get(ConsOf[AST](name))
|
||||||
}
|
}
|
||||||
object Mod {
|
object Mod {
|
||||||
private val pool = new Pool[ModOf[AST]]()
|
private val pool = new Pool[ModOf[AST]]()
|
||||||
val any = UnapplyByType[Mod]
|
val any = UnapplyByType[Mod]
|
||||||
def unapply(t: AST) = Unapply[Mod].run(_.name)(t)
|
def unapply(t: AST) = Unapply[Mod].run(_.name)(t)
|
||||||
def apply(name: String): Mod = pool.get(ModOf[AST](name))
|
def apply(name: String): Mod = pool.get(ModOf[AST](name))
|
||||||
}
|
}
|
||||||
object Opr {
|
object Opr {
|
||||||
private val pool = new Pool[OprOf[AST]]()
|
private val pool = new Pool[OprOf[AST]]()
|
||||||
val app = Opr(" ")
|
val app = Opr(" ")
|
||||||
val any = UnapplyByType[Opr]
|
val any = UnapplyByType[Opr]
|
||||||
def unapply(t: AST) = Unapply[Opr].run(_.name)(t)
|
def unapply(t: AST) = Unapply[Opr].run(_.name)(t)
|
||||||
def apply(name: String): Opr = pool.get(OprOf[AST](name))
|
def apply(name: String): Opr = pool.get(OprOf[AST](name))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,8 +523,8 @@ object AST {
|
|||||||
extends InvalidOf[T]
|
extends InvalidOf[T]
|
||||||
with Phantom
|
with Phantom
|
||||||
object InvalidSuffixOf {
|
object InvalidSuffixOf {
|
||||||
implicit def ftor: Functor[InvalidSuffixOf] = semi.functor
|
implicit def ftor: Functor[InvalidSuffixOf] = semi.functor
|
||||||
implicit def fold: Foldable[InvalidSuffixOf] = semi.foldable
|
implicit def fold: Foldable[InvalidSuffixOf] = semi.foldable
|
||||||
implicit def ozip[T]: OffsetZip[InvalidSuffixOf, T] = t => t.coerce
|
implicit def ozip[T]: OffsetZip[InvalidSuffixOf, T] = t => t.coerce
|
||||||
implicit def repr[T]: Repr[InvalidSuffixOf[T]] =
|
implicit def repr[T]: Repr[InvalidSuffixOf[T]] =
|
||||||
t => R + t.elem + t.suffix
|
t => R + t.elem + t.suffix
|
||||||
@ -598,12 +575,12 @@ object AST {
|
|||||||
|
|
||||||
//// Smart Constructors ////
|
//// Smart Constructors ////
|
||||||
|
|
||||||
def apply(i: String): Number = Number(None, i)
|
def apply(i: String): Number = Number(None, i)
|
||||||
def apply(b: String, i: String): Number = Number(Some(b), i)
|
def apply(b: String, i: String): Number = Number(Some(b), i)
|
||||||
def apply(i: Int): Number = Number(i.toString)
|
def apply(i: Int): Number = Number(i.toString)
|
||||||
def apply(b: Int, i: String): Number = Number(b.toString, i)
|
def apply(b: Int, i: String): Number = Number(b.toString, i)
|
||||||
def apply(b: String, i: Int): Number = Number(b, i.toString)
|
def apply(b: String, i: Int): Number = Number(b, i.toString)
|
||||||
def apply(b: Int, i: Int): Number = Number(b.toString, i.toString)
|
def apply(b: Int, i: Int): Number = Number(b.toString, i.toString)
|
||||||
def apply(b: Option[String], i: String): Number =
|
def apply(b: Option[String], i: String): Number =
|
||||||
NumberOf[AST](b, i)
|
NumberOf[AST](b, i)
|
||||||
def unapply(t: AST) = Unapply[Number].run(t => (t.base, t.int))(t)
|
def unapply(t: AST) = Unapply[Number].run(t => (t.base, t.int))(t)
|
||||||
@ -616,14 +593,14 @@ object AST {
|
|||||||
extends InvalidOf[T]
|
extends InvalidOf[T]
|
||||||
with Phantom
|
with Phantom
|
||||||
object DanglingBase {
|
object DanglingBase {
|
||||||
val any = UnapplyByType[DanglingBase]
|
val any = UnapplyByType[DanglingBase]
|
||||||
def apply(base: String): DanglingBase = DanglingBaseOf[AST](base)
|
def apply(base: String): DanglingBase = DanglingBaseOf[AST](base)
|
||||||
def unapply(t: AST) =
|
def unapply(t: AST) =
|
||||||
Unapply[DanglingBase].run(_.base)(t)
|
Unapply[DanglingBase].run(_.base)(t)
|
||||||
}
|
}
|
||||||
object DanglingBaseOf {
|
object DanglingBaseOf {
|
||||||
implicit def ftor: Functor[DanglingBaseOf] = semi.functor
|
implicit def ftor: Functor[DanglingBaseOf] = semi.functor
|
||||||
implicit def fold: Foldable[DanglingBaseOf] = semi.foldable
|
implicit def fold: Foldable[DanglingBaseOf] = semi.foldable
|
||||||
implicit def ozip[T]: OffsetZip[DanglingBaseOf, T] = t => t.coerce
|
implicit def ozip[T]: OffsetZip[DanglingBaseOf, T] = t => t.coerce
|
||||||
implicit def repr[T]: Repr[DanglingBaseOf[T]] = R + _.base + '_'
|
implicit def repr[T]: Repr[DanglingBaseOf[T]] = R + _.base + '_'
|
||||||
}
|
}
|
||||||
@ -632,10 +609,10 @@ object AST {
|
|||||||
//// Instances ////
|
//// Instances ////
|
||||||
|
|
||||||
object NumberOf {
|
object NumberOf {
|
||||||
implicit def fromInt[T](int: Int): Number = Number(int)
|
implicit def fromInt[T](int: Int): Number = Number(int)
|
||||||
implicit def ftor: Functor[NumberOf] = semi.functor
|
implicit def ftor: Functor[NumberOf] = semi.functor
|
||||||
implicit def fold: Foldable[NumberOf] = semi.foldable
|
implicit def fold: Foldable[NumberOf] = semi.foldable
|
||||||
implicit def ozip[T]: OffsetZip[NumberOf, T] = t => t.coerce
|
implicit def ozip[T]: OffsetZip[NumberOf, T] = t => t.coerce
|
||||||
implicit def repr[T]: Repr[NumberOf[T]] =
|
implicit def repr[T]: Repr[NumberOf[T]] =
|
||||||
t => t.base.map(_ + "_").getOrElse("") + t.int
|
t => t.base.map(_ + "_").getOrElse("") + t.int
|
||||||
}
|
}
|
||||||
@ -671,7 +648,7 @@ object AST {
|
|||||||
|
|
||||||
//// Definition ////
|
//// Definition ////
|
||||||
|
|
||||||
sealed trait Line[T] extends TextOf[T]
|
sealed trait Line[T] extends TextOf[T]
|
||||||
sealed trait Block[T] extends TextOf[T]
|
sealed trait Block[T] extends TextOf[T]
|
||||||
|
|
||||||
final case class UnclosedOf[T](line: Line[T])
|
final case class UnclosedOf[T](line: Line[T])
|
||||||
@ -689,15 +666,36 @@ object AST {
|
|||||||
with Phantom
|
with Phantom
|
||||||
|
|
||||||
object Line {
|
object Line {
|
||||||
final case class Raw[T](text: List[Segment._Raw[T]])
|
/* Note [Circe and naming] */
|
||||||
|
val Raw = LineRaw
|
||||||
|
/* Note [Circe and naming] */
|
||||||
|
type Raw[T] = LineRaw[T]
|
||||||
|
|
||||||
|
/* Note [Circe and naming] */
|
||||||
|
val Fmt = LineFmt
|
||||||
|
/* Note [Circe and naming] */
|
||||||
|
type Fmt[T] = LineFmt[T]
|
||||||
|
|
||||||
|
/* Note [Circe and naming] */
|
||||||
|
final case class LineRaw[T](text: List[Segment._Raw[T]])
|
||||||
extends Line[T]
|
extends Line[T]
|
||||||
with Phantom {
|
with Phantom {
|
||||||
val quote = '"'
|
val quote = '"'
|
||||||
}
|
}
|
||||||
final case class Fmt[T](text: List[Segment._Fmt[T]]) extends Line[T] {
|
/* Note [Circe and naming] */
|
||||||
|
final case class LineFmt[T](text: List[Segment._Fmt[T]])
|
||||||
|
extends Line[T] {
|
||||||
val quote = '\''
|
val quote = '\''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Note [Circe and naming]
|
||||||
|
* ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
* To be able to use Circe automatic derivation for traits, all case
|
||||||
|
* classes in its subtree must bear unique names. So `Line.Fmt` and
|
||||||
|
* `Line.Raw` cannot be used as they would collide with [[Block.Raw]]
|
||||||
|
* and [[Block.Fmt]].
|
||||||
|
*/
|
||||||
|
|
||||||
////// INSTANCES /////
|
////// INSTANCES /////
|
||||||
import Segment.implicits._
|
import Segment.implicits._
|
||||||
|
|
||||||
@ -707,9 +705,9 @@ object AST {
|
|||||||
case t: Raw[T] => t.quote + t.text + t.quote
|
case t: Raw[T] => t.quote + t.text + t.quote
|
||||||
case t: Fmt[T] => t.quote + t.text + t.quote
|
case t: Fmt[T] => t.quote + t.text + t.quote
|
||||||
}
|
}
|
||||||
implicit def ozip[T: Repr]: OffsetZip[Line, T] = {
|
implicit def ozip[T: Repr]: OffsetZip[Line, T] = {
|
||||||
case t: Raw[T] => t.coerce
|
case t: Raw[T] => t.coerce
|
||||||
case t: Fmt[T] =>
|
case t: Fmt[T] =>
|
||||||
var offset = Index(t.quote.span)
|
var offset = Index(t.quote.span)
|
||||||
val text2 = for (elem <- t.text) yield {
|
val text2 = for (elem <- t.text) yield {
|
||||||
val offElem = elem.map(offset -> _)
|
val offElem = elem.map(offset -> _)
|
||||||
@ -730,7 +728,7 @@ object AST {
|
|||||||
with Phantom {
|
with Phantom {
|
||||||
val quote = "\"\"\""
|
val quote = "\"\"\""
|
||||||
}
|
}
|
||||||
case class Fmt[T](
|
final case class Fmt[T](
|
||||||
text: List[Line[Segment._Fmt[T]]],
|
text: List[Line[Segment._Fmt[T]]],
|
||||||
spaces: Int,
|
spaces: Int,
|
||||||
offset: Int
|
offset: Int
|
||||||
@ -755,7 +753,7 @@ object AST {
|
|||||||
case Fmt(text, s, off) => q + s + text.map(line(off, _))
|
case Fmt(text, s, off) => q + s + text.map(line(off, _))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
implicit def ozip[T: Repr]: OffsetZip[Block, T] = {
|
implicit def ozip[T: Repr]: OffsetZip[Block, T] = {
|
||||||
case body: Raw[T] => body.coerce
|
case body: Raw[T] => body.coerce
|
||||||
case body: Fmt[T] =>
|
case body: Fmt[T] =>
|
||||||
var offset = Index(body.quote.span)
|
var offset = Index(body.quote.span)
|
||||||
@ -802,13 +800,14 @@ object AST {
|
|||||||
def apply(quote: String): InlineBlock = InlineBlockOf[AST](quote)
|
def apply(quote: String): InlineBlock = InlineBlockOf[AST](quote)
|
||||||
}
|
}
|
||||||
|
|
||||||
def apply(text: TextOf[AST]): Text = text
|
def apply(text: TextOf[AST]): Text = text
|
||||||
def apply(segment: Segment.Fmt*): Text = Text(Line.Fmt(segment.to[List]))
|
def apply(segment: Segment.Fmt*): Text = Text(Line.Fmt(segment.to[List]))
|
||||||
def apply(spaces: Int, off: Int, line: Block.Line[Segment.Fmt]*): Text =
|
def apply(spaces: Int, off: Int, line: Block.Line[Segment.Fmt]*): Text =
|
||||||
Text(Block.Fmt(line.to[List], spaces, off))
|
Text(Block.Fmt(line.to[List], spaces, off))
|
||||||
|
|
||||||
object Raw {
|
object Raw {
|
||||||
def apply(segment: Segment.Raw*): Text = Text(Line.Raw(segment.to[List]))
|
def apply(segment: Segment.Raw*): Text =
|
||||||
|
Text(Line.Raw(segment.to[List]))
|
||||||
def apply(spaces: Int, off: Int, line: Block.Line[Segment.Raw]*): Text =
|
def apply(spaces: Int, off: Int, line: Block.Line[Segment.Raw]*): Text =
|
||||||
Text(Block.Raw(line.to[List], spaces, off))
|
Text(Block.Raw(line.to[List], spaces, off))
|
||||||
}
|
}
|
||||||
@ -828,15 +827,15 @@ object AST {
|
|||||||
t => t.copy(line = OffsetZip(t.line))
|
t => t.copy(line = OffsetZip(t.line))
|
||||||
}
|
}
|
||||||
object InvalidQuoteOf {
|
object InvalidQuoteOf {
|
||||||
implicit def ftor: Functor[InvalidQuoteOf] = semi.functor
|
implicit def ftor: Functor[InvalidQuoteOf] = semi.functor
|
||||||
implicit def fold: Foldable[InvalidQuoteOf] = semi.foldable
|
implicit def fold: Foldable[InvalidQuoteOf] = semi.foldable
|
||||||
implicit def repr[T: Repr]: Repr[InvalidQuoteOf[T]] = _.quote
|
implicit def repr[T: Repr]: Repr[InvalidQuoteOf[T]] = _.quote
|
||||||
implicit def ozip[T: Repr]: OffsetZip[InvalidQuoteOf, T] = t => t.coerce
|
implicit def ozip[T: Repr]: OffsetZip[InvalidQuoteOf, T] = t => t.coerce
|
||||||
}
|
}
|
||||||
object InlineBlockOf {
|
object InlineBlockOf {
|
||||||
implicit def ftor: Functor[InlineBlockOf] = semi.functor
|
implicit def ftor: Functor[InlineBlockOf] = semi.functor
|
||||||
implicit def fold: Foldable[InlineBlockOf] = semi.foldable
|
implicit def fold: Foldable[InlineBlockOf] = semi.foldable
|
||||||
implicit def repr[T: Repr]: Repr[InlineBlockOf[T]] = _.quote
|
implicit def repr[T: Repr]: Repr[InlineBlockOf[T]] = _.quote
|
||||||
implicit def ozip[T: Repr]: OffsetZip[InlineBlockOf, T] = t => t.coerce
|
implicit def ozip[T: Repr]: OffsetZip[InlineBlockOf, T] = t => t.coerce
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -862,7 +861,7 @@ object AST {
|
|||||||
final case class _Escape[T](code: Escape) extends _Fmt[T] with Phantom
|
final case class _Escape[T](code: Escape) extends _Fmt[T] with Phantom
|
||||||
|
|
||||||
object Expr { def apply(t: Option[AST]): Fmt = _Expr(t) }
|
object Expr { def apply(t: Option[AST]): Fmt = _Expr(t) }
|
||||||
object Plain { def apply(s: String): Raw = _Plain(s) }
|
object Plain { def apply(s: String): Raw = _Plain(s) }
|
||||||
|
|
||||||
//// Instances ////
|
//// Instances ////
|
||||||
|
|
||||||
@ -875,20 +874,20 @@ object AST {
|
|||||||
t => R + ("\\" + t.code.repr)
|
t => R + ("\\" + t.code.repr)
|
||||||
implicit def ozipEscape[T]: OffsetZip[_Escape, T] = t => t.coerce
|
implicit def ozipEscape[T]: OffsetZip[_Escape, T] = t => t.coerce
|
||||||
|
|
||||||
implicit def foldPlain: Foldable[_Plain] = semi.foldable
|
implicit def foldPlain: Foldable[_Plain] = semi.foldable
|
||||||
implicit def ftorPlain[T]: Functor[_Plain] = semi.functor
|
implicit def ftorPlain[T]: Functor[_Plain] = semi.functor
|
||||||
implicit def reprPlain[T]: Repr[_Plain[T]] = _.value
|
implicit def reprPlain[T]: Repr[_Plain[T]] = _.value
|
||||||
implicit def ozipPlain[T]: OffsetZip[_Plain, T] = t => t.coerce
|
implicit def ozipPlain[T]: OffsetZip[_Plain, T] = t => t.coerce
|
||||||
|
|
||||||
implicit def ftorExpr[T]: Functor[_Expr] = semi.functor
|
implicit def ftorExpr[T]: Functor[_Expr] = semi.functor
|
||||||
implicit def foldExpr: Foldable[_Expr] = semi.foldable
|
implicit def foldExpr: Foldable[_Expr] = semi.foldable
|
||||||
implicit def reprExpr[T: Repr]: Repr[_Expr[T]] =
|
implicit def reprExpr[T: Repr]: Repr[_Expr[T]] =
|
||||||
R + '`' + _.value + '`'
|
R + '`' + _.value + '`'
|
||||||
implicit def ozipExpr[T]: OffsetZip[_Expr, T] =
|
implicit def ozipExpr[T]: OffsetZip[_Expr, T] =
|
||||||
_.map(Index.Start -> _)
|
_.map(Index.Start -> _)
|
||||||
|
|
||||||
implicit def ftorRaw[T]: Functor[_Raw] = semi.functor
|
implicit def ftorRaw[T]: Functor[_Raw] = semi.functor
|
||||||
implicit def foldRaw: Foldable[_Raw] = semi.foldable
|
implicit def foldRaw: Foldable[_Raw] = semi.foldable
|
||||||
implicit def reprRaw[T]: Repr[_Raw[T]] = {
|
implicit def reprRaw[T]: Repr[_Raw[T]] = {
|
||||||
case t: _Plain[T] => Repr(t)
|
case t: _Plain[T] => Repr(t)
|
||||||
}
|
}
|
||||||
@ -896,8 +895,8 @@ object AST {
|
|||||||
case t: _Plain[T] => OffsetZip(t)
|
case t: _Plain[T] => OffsetZip(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit def ftorFmt[T]: Functor[_Fmt] = semi.functor
|
implicit def ftorFmt[T]: Functor[_Fmt] = semi.functor
|
||||||
implicit def foldFmt: Foldable[_Fmt] = semi.foldable
|
implicit def foldFmt: Foldable[_Fmt] = semi.foldable
|
||||||
implicit def reprFmt[T: Repr]: Repr[_Fmt[T]] = {
|
implicit def reprFmt[T: Repr]: Repr[_Fmt[T]] = {
|
||||||
case t: _Plain[T] => Repr(t)
|
case t: _Plain[T] => Repr(t)
|
||||||
case t: _Expr[T] => Repr(t)
|
case t: _Expr[T] => Repr(t)
|
||||||
@ -955,10 +954,10 @@ object AST {
|
|||||||
//// Smart Constructors ////
|
//// Smart Constructors ////
|
||||||
|
|
||||||
object Prefix {
|
object Prefix {
|
||||||
val any = UnapplyByType[Prefix]
|
val any = UnapplyByType[Prefix]
|
||||||
def unapply(t: AST) = Unapply[Prefix].run(t => (t.fn, t.arg))(t)
|
def unapply(t: AST) = Unapply[Prefix].run(t => (t.fn, t.arg))(t)
|
||||||
def apply(fn: AST, off: Int, arg: AST): Prefix = PrefixOf(fn, off, arg)
|
def apply(fn: AST, off: Int, arg: AST): Prefix = PrefixOf(fn, off, arg)
|
||||||
def apply(fn: AST, arg: AST): Prefix = Prefix(fn, 1, arg)
|
def apply(fn: AST, arg: AST): Prefix = Prefix(fn, 1, arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
object Infix {
|
object Infix {
|
||||||
@ -1040,18 +1039,18 @@ object AST {
|
|||||||
def unapply(t: AST) = Unapply[Left].run(t => (t.arg, t.opr))(t)
|
def unapply(t: AST) = Unapply[Left].run(t => (t.arg, t.opr))(t)
|
||||||
|
|
||||||
def apply(arg: AST, off: Int, opr: Opr): Left = LeftOf(arg, off, opr)
|
def apply(arg: AST, off: Int, opr: Opr): Left = LeftOf(arg, off, opr)
|
||||||
def apply(arg: AST, opr: Opr): Left = Left(arg, 1, opr)
|
def apply(arg: AST, opr: Opr): Left = Left(arg, 1, opr)
|
||||||
}
|
}
|
||||||
object Right {
|
object Right {
|
||||||
val any = UnapplyByType[Right]
|
val any = UnapplyByType[Right]
|
||||||
def unapply(t: AST) = Unapply[Right].run(t => (t.opr, t.arg))(t)
|
def unapply(t: AST) = Unapply[Right].run(t => (t.opr, t.arg))(t)
|
||||||
|
|
||||||
def apply(opr: Opr, off: Int, arg: AST): Right = RightOf(opr, off, arg)
|
def apply(opr: Opr, off: Int, arg: AST): Right = RightOf(opr, off, arg)
|
||||||
def apply(opr: Opr, arg: AST): Right = Right(opr, 1, arg)
|
def apply(opr: Opr, arg: AST): Right = Right(opr, 1, arg)
|
||||||
}
|
}
|
||||||
object Sides {
|
object Sides {
|
||||||
val any = UnapplyByType[Sides]
|
val any = UnapplyByType[Sides]
|
||||||
def unapply(t: AST) = Unapply[Sides].run(_.opr)(t)
|
def unapply(t: AST) = Unapply[Sides].run(_.opr)(t)
|
||||||
def apply(opr: Opr): Sides = SidesOf[AST](opr)
|
def apply(opr: Opr): Sides = SidesOf[AST](opr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1074,10 +1073,10 @@ object AST {
|
|||||||
t => t.copy(arg = (Index(t.opr.span + t.off), t.arg))
|
t => t.copy(arg = (Index(t.opr.span + t.off), t.arg))
|
||||||
}
|
}
|
||||||
object SidesOf {
|
object SidesOf {
|
||||||
implicit def ftor: Functor[SidesOf] = semi.functor
|
implicit def ftor: Functor[SidesOf] = semi.functor
|
||||||
implicit def fold: Foldable[SidesOf] = semi.foldable
|
implicit def fold: Foldable[SidesOf] = semi.foldable
|
||||||
implicit def repr[T: Repr]: Repr[SidesOf[T]] = t => R + t.opr
|
implicit def repr[T: Repr]: Repr[SidesOf[T]] = t => R + t.opr
|
||||||
implicit def ozip[T]: OffsetZip[SidesOf, T] = t => t.coerce
|
implicit def ozip[T]: OffsetZip[SidesOf, T] = t => t.coerce
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1155,26 +1154,26 @@ object AST {
|
|||||||
def toOptional: LineOf[Option[T]] = copy(elem = Some(elem))
|
def toOptional: LineOf[Option[T]] = copy(elem = Some(elem))
|
||||||
}
|
}
|
||||||
object LineOf {
|
object LineOf {
|
||||||
implicit def ftorLine: Functor[LineOf] = semi.functor
|
implicit def ftorLine: Functor[LineOf] = semi.functor
|
||||||
implicit def fold: Foldable[LineOf] = semi.foldable
|
implicit def fold: Foldable[LineOf] = semi.foldable
|
||||||
implicit def reprLine[T: Repr]: Repr[LineOf[T]] = t => R + t.elem + t.off
|
implicit def reprLine[T: Repr]: Repr[LineOf[T]] = t => R + t.elem + t.off
|
||||||
}
|
}
|
||||||
object Line {
|
object Line {
|
||||||
// FIXME: Compatibility mode
|
// FIXME: Compatibility mode
|
||||||
type NonEmpty = Line
|
type NonEmpty = Line
|
||||||
val Required = Line
|
val Required = Line
|
||||||
def apply[T](elem: T, off: Int) = LineOf(elem, off)
|
def apply[T](elem: T, off: Int) = LineOf(elem, off)
|
||||||
def apply[T](elem: T): LineOf[T] = LineOf(elem, 0)
|
def apply[T](elem: T): LineOf[T] = LineOf(elem, 0)
|
||||||
}
|
}
|
||||||
object OptLine {
|
object OptLine {
|
||||||
def apply(): OptLine = Line(None, 0)
|
def apply(): OptLine = Line(None, 0)
|
||||||
def apply(elem: AST): OptLine = Line(Some(elem))
|
def apply(elem: AST): OptLine = Line(Some(elem))
|
||||||
def apply(off: Int): OptLine = Line(None, off)
|
def apply(off: Int): OptLine = Line(None, off)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
object BlockOf {
|
object BlockOf {
|
||||||
implicit def ftorBlock: Functor[BlockOf] = semi.functor
|
implicit def ftorBlock: Functor[BlockOf] = semi.functor
|
||||||
implicit def fold: Foldable[BlockOf] = semi.foldable
|
implicit def fold: Foldable[BlockOf] = semi.foldable
|
||||||
implicit def reprBlock[T: Repr]: Repr[BlockOf[T]] = t => {
|
implicit def reprBlock[T: Repr]: Repr[BlockOf[T]] = t => {
|
||||||
val headRepr = if (t.isOrphan) R else newline
|
val headRepr = if (t.isOrphan) R else newline
|
||||||
val emptyLinesRepr = t.emptyLines.map(R + _ + newline)
|
val emptyLinesRepr = t.emptyLines.map(R + _ + newline)
|
||||||
@ -1207,11 +1206,11 @@ object AST {
|
|||||||
object Module {
|
object Module {
|
||||||
import Block._
|
import Block._
|
||||||
type M = Module
|
type M = Module
|
||||||
val any = UnapplyByType[M]
|
val any = UnapplyByType[M]
|
||||||
def unapply(t: AST) = Unapply[M].run(_.lines)(t)
|
def unapply(t: AST) = Unapply[M].run(_.lines)(t)
|
||||||
def apply(ls: List1[OptLine]): M = ModuleOf(ls)
|
def apply(ls: List1[OptLine]): M = ModuleOf(ls)
|
||||||
def apply(l: OptLine): M = Module(List1(l))
|
def apply(l: OptLine): M = Module(List1(l))
|
||||||
def apply(l: OptLine, ls: OptLine*): M = Module(List1(l, ls.to[List]))
|
def apply(l: OptLine, ls: OptLine*): M = Module(List1(l, ls.to[List]))
|
||||||
def apply(l: OptLine, ls: List[OptLine]): M = Module(List1(l, ls))
|
def apply(l: OptLine, ls: List[OptLine]): M = Module(List1(l, ls))
|
||||||
def traverseWithOff(m: M)(f: (Index, AST) => AST): M = {
|
def traverseWithOff(m: M)(f: (Index, AST) => AST): M = {
|
||||||
val lines2 = m.lines.map { line: OptLine =>
|
val lines2 = m.lines.map { line: OptLine =>
|
||||||
@ -1222,8 +1221,8 @@ object AST {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
object ModuleOf {
|
object ModuleOf {
|
||||||
implicit def ftor: Functor[ModuleOf] = semi.functor
|
implicit def ftor: Functor[ModuleOf] = semi.functor
|
||||||
implicit def fold: Foldable[ModuleOf] = semi.foldable
|
implicit def fold: Foldable[ModuleOf] = semi.foldable
|
||||||
implicit def ozip[T]: OffsetZip[ModuleOf, T] = _.map(Index.Start -> _)
|
implicit def ozip[T]: OffsetZip[ModuleOf, T] = _.map(Index.Start -> _)
|
||||||
implicit def repr[T: Repr]: Repr[ModuleOf[T]] =
|
implicit def repr[T: Repr]: Repr[ModuleOf[T]] =
|
||||||
t => R + t.lines.head + t.lines.tail.map(newline + _)
|
t => R + t.lines.head + t.lines.tail.map(newline + _)
|
||||||
@ -1323,14 +1322,14 @@ object AST {
|
|||||||
|
|
||||||
final case class Segment(head: AST, body: Option[SAST])
|
final case class Segment(head: AST, body: Option[SAST])
|
||||||
object Segment {
|
object Segment {
|
||||||
def apply(head: AST): Segment = Segment(head, None)
|
def apply(head: AST): Segment = Segment(head, None)
|
||||||
implicit def repr: Repr[Segment] = t => R + t.head + t.body
|
implicit def repr: Repr[Segment] = t => R + t.head + t.body
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object AmbiguousOf {
|
object AmbiguousOf {
|
||||||
implicit def ftor: Functor[AmbiguousOf] = semi.functor
|
implicit def ftor: Functor[AmbiguousOf] = semi.functor
|
||||||
implicit def fold: Foldable[AmbiguousOf] = semi.foldable
|
implicit def fold: Foldable[AmbiguousOf] = semi.foldable
|
||||||
implicit def repr[T]: Repr[AmbiguousOf[T]] = t => R + t.segs.map(Repr(_))
|
implicit def repr[T]: Repr[AmbiguousOf[T]] = t => R + t.segs.map(Repr(_))
|
||||||
implicit def ozip[T]: OffsetZip[AmbiguousOf, T] = _.map(Index.Start -> _)
|
implicit def ozip[T]: OffsetZip[AmbiguousOf, T] = _.map(Index.Start -> _)
|
||||||
}
|
}
|
||||||
@ -1514,8 +1513,8 @@ object AST {
|
|||||||
extends SpacelessASTOf[T]
|
extends SpacelessASTOf[T]
|
||||||
with Phantom
|
with Phantom
|
||||||
object Comment {
|
object Comment {
|
||||||
val any = UnapplyByType[Comment]
|
val any = UnapplyByType[Comment]
|
||||||
val symbol = "#"
|
val symbol = "#"
|
||||||
def apply(lines: List[String]): Comment = ASTOf(CommentOf(lines))
|
def apply(lines: List[String]): Comment = ASTOf(CommentOf(lines))
|
||||||
def unapply(t: AST): Option[List[String]] =
|
def unapply(t: AST): Option[List[String]] =
|
||||||
Unapply[Comment].run(t => t.lines)(t)
|
Unapply[Comment].run(t => t.lines)(t)
|
||||||
@ -1538,7 +1537,7 @@ object AST {
|
|||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
type Documented = ASTOf[DocumentedOf]
|
type Documented = ASTOf[DocumentedOf]
|
||||||
case class DocumentedOf[T](doc: Doc, emptyLinesBetween: Int, ast: T)
|
final case class DocumentedOf[T](doc: Doc, emptyLinesBetween: Int, ast: T)
|
||||||
extends ShapeOf[T]
|
extends ShapeOf[T]
|
||||||
object Documented {
|
object Documented {
|
||||||
val any = UnapplyByType[Documented]
|
val any = UnapplyByType[Documented]
|
||||||
@ -1560,6 +1559,9 @@ object AST {
|
|||||||
}
|
}
|
||||||
implicit def offsetZip[T]: OffsetZip[DocumentedOf, T] =
|
implicit def offsetZip[T]: OffsetZip[DocumentedOf, T] =
|
||||||
_.map(Index.Start -> _)
|
_.map(Index.Start -> _)
|
||||||
|
|
||||||
|
implicit def toJson[T]: Encoder[DocumentedOf[T]] =
|
||||||
|
_ => throw new NotImplementedError()
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
@ -1569,10 +1571,10 @@ object AST {
|
|||||||
type Import = ASTOf[ImportOf]
|
type Import = ASTOf[ImportOf]
|
||||||
final case class ImportOf[T](path: List1[Cons]) extends SpacelessASTOf[T]
|
final case class ImportOf[T](path: List1[Cons]) extends SpacelessASTOf[T]
|
||||||
object Import {
|
object Import {
|
||||||
def apply(path: List1[Cons]): Import = ImportOf[AST](path)
|
def apply(path: List1[Cons]): Import = ImportOf[AST](path)
|
||||||
def apply(head: Cons): Import = Import(head, List())
|
def apply(head: Cons): Import = Import(head, List())
|
||||||
def apply(head: Cons, tail: List[Cons]): Import = Import(List1(head, tail))
|
def apply(head: Cons, tail: List[Cons]): Import = Import(List1(head, tail))
|
||||||
def apply(head: Cons, tail: Cons*): Import = Import(head, tail.toList)
|
def apply(head: Cons, tail: Cons*): Import = Import(head, tail.toList)
|
||||||
def unapply(t: AST): Option[List1[Cons]] =
|
def unapply(t: AST): Option[List1[Cons]] =
|
||||||
Unapply[Import].run(t => t.path)(t)
|
Unapply[Import].run(t => t.path)(t)
|
||||||
val any = UnapplyByType[Import]
|
val any = UnapplyByType[Import]
|
||||||
@ -1621,12 +1623,12 @@ object AST {
|
|||||||
type Group = ASTOf[GroupOf]
|
type Group = ASTOf[GroupOf]
|
||||||
final case class GroupOf[T](body: Option[T]) extends SpacelessASTOf[T]
|
final case class GroupOf[T](body: Option[T]) extends SpacelessASTOf[T]
|
||||||
object Group {
|
object Group {
|
||||||
val any = UnapplyByType[Group]
|
val any = UnapplyByType[Group]
|
||||||
def unapply(t: AST) = Unapply[Group].run(_.body)(t)
|
def unapply(t: AST) = Unapply[Group].run(_.body)(t)
|
||||||
def apply(body: Option[AST]): Group = GroupOf(body)
|
def apply(body: Option[AST]): Group = GroupOf(body)
|
||||||
def apply(body: AST): Group = Group(Some(body))
|
def apply(body: AST): Group = Group(Some(body))
|
||||||
def apply(body: SAST): Group = Group(body.el)
|
def apply(body: SAST): Group = Group(body.el)
|
||||||
def apply(): Group = Group(None)
|
def apply(): Group = Group(None)
|
||||||
}
|
}
|
||||||
object GroupOf {
|
object GroupOf {
|
||||||
implicit def ftor: Functor[GroupOf] = semi.functor
|
implicit def ftor: Functor[GroupOf] = semi.functor
|
||||||
@ -1645,9 +1647,9 @@ object AST {
|
|||||||
final case class DefOf[T](name: Cons, args: List[T], body: Option[T])
|
final case class DefOf[T](name: Cons, args: List[T], body: Option[T])
|
||||||
extends SpacelessASTOf[T]
|
extends SpacelessASTOf[T]
|
||||||
object Def {
|
object Def {
|
||||||
val any = UnapplyByType[Def]
|
val any = UnapplyByType[Def]
|
||||||
val symbol = "def"
|
val symbol = "def"
|
||||||
def apply(name: Cons): Def = Def(name, List())
|
def apply(name: Cons): Def = Def(name, List())
|
||||||
def apply(name: Cons, args: List[AST]): Def = Def(name, args, None)
|
def apply(name: Cons, args: List[AST]): Def = Def(name, args, None)
|
||||||
def apply(name: Cons, args: List[AST], body: Option[AST]): Def =
|
def apply(name: Cons, args: List[AST], body: Option[AST]): Def =
|
||||||
DefOf(name, args, body)
|
DefOf(name, args, body)
|
||||||
@ -1688,6 +1690,41 @@ object AST {
|
|||||||
implicit def ozip[T]: OffsetZip[ForeignOf, T] = _.map(Index.Start -> _)
|
implicit def ozip[T]: OffsetZip[ForeignOf, T] = _.map(Index.Start -> _)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//// ASTClass ////
|
||||||
|
|
||||||
|
/** [[ASTClass]] implements set of AST operations based on a precise AST
|
||||||
|
* shape. Because the [[T]] parameter in [[ASTOf]] is covariant, we may lose
|
||||||
|
* information about the shape after we construct the AST, thus this instance
|
||||||
|
* is used to cache all necessary operations during AST construction.
|
||||||
|
*/
|
||||||
|
sealed trait ASTClass[T[_]] {
|
||||||
|
def repr(t: T[AST]): Repr.Builder
|
||||||
|
def map(t: T[AST])(f: AST => AST): T[AST]
|
||||||
|
def mapWithOff(t: T[AST])(f: (Index, AST) => AST): T[AST]
|
||||||
|
def zipWithOffset(t: T[AST]): T[(Index, AST)]
|
||||||
|
def encode(t: T[AST]): Json
|
||||||
|
}
|
||||||
|
object ASTClass {
|
||||||
|
def apply[T[_]](implicit cls: ASTClass[T]): ASTClass[T] = cls
|
||||||
|
implicit def instance[T[S] <: ShapeOf[S]](
|
||||||
|
implicit
|
||||||
|
evRepr: Repr[T[AST]],
|
||||||
|
evFtor: Functor[T],
|
||||||
|
evOzip: OffsetZip[T, AST]
|
||||||
|
): ASTClass[T] =
|
||||||
|
new ASTClass[T] {
|
||||||
|
def repr(t: T[AST]): Repr.Builder = evRepr.repr(t)
|
||||||
|
def map(t: T[AST])(f: AST => AST): T[AST] = Functor[T].map(t)(f)
|
||||||
|
def zipWithOffset(t: T[AST]): T[(Index, AST)] = OffsetZip(t)
|
||||||
|
def mapWithOff(t: T[AST])(f: (Index, AST) => AST): T[AST] =
|
||||||
|
Functor[T].map(zipWithOffset(t))(f.tupled)
|
||||||
|
def encode(t: T[AST]): Json = {
|
||||||
|
val shapeEncoder = implicitly[Encoder[ShapeOf[AST]]]
|
||||||
|
shapeEncoder(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
@ -1738,4 +1775,4 @@ object AST {
|
|||||||
val v1_x = vx.as[Var]
|
val v1_x = vx.as[Var]
|
||||||
println(v1_x)
|
println(v1_x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,8 @@ import org.enso.syntax.text.AST.SAST
|
|||||||
import org.enso.syntax.text.prec.Operator
|
import org.enso.syntax.text.prec.Operator
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import org.enso.data.{Index, Shifted}
|
import org.enso.data.Index
|
||||||
|
import org.enso.data.Shifted
|
||||||
import org.enso.syntax.text.ast.Repr
|
import org.enso.syntax.text.ast.Repr
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -13,7 +14,9 @@ import org.enso.syntax.text.ast.Repr
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
object Pattern {
|
object Pattern {
|
||||||
import cats.{Foldable, Functor, Traverse}
|
import cats.Foldable
|
||||||
|
import cats.Functor
|
||||||
|
import cats.Traverse
|
||||||
import cats.derived._
|
import cats.derived._
|
||||||
|
|
||||||
type P = Pattern
|
type P = Pattern
|
||||||
@ -34,7 +37,7 @@ object Pattern {
|
|||||||
(nStream.reverse, nOff)
|
(nStream.reverse, nOff)
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Class
|
sealed trait Class
|
||||||
object Class {
|
object Class {
|
||||||
final case object Normal extends Class
|
final case object Normal extends Class
|
||||||
final case object Pattern extends Class
|
final case object Pattern extends Class
|
||||||
@ -195,7 +198,7 @@ object Pattern {
|
|||||||
|
|
||||||
//// Result ////
|
//// Result ////
|
||||||
|
|
||||||
case class Result(elem: Match, stream: AST.Stream) {
|
final case class Result(elem: Match, stream: AST.Stream) {
|
||||||
def map(fn: Match => Match): Result = copy(elem = fn(elem))
|
def map(fn: Match => Match): Result = copy(elem = fn(elem))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.enso.syntax.text.ast.text
|
package org.enso.syntax.text.ast.text
|
||||||
|
|
||||||
import org.enso.flexer.ADT
|
import org.enso.flexer.ADT
|
||||||
|
import org.enso.syntax.text.ast.text.Escape.Slash.toString
|
||||||
|
|
||||||
sealed trait Escape {
|
sealed trait Escape {
|
||||||
val repr: String
|
val repr: String
|
||||||
@ -8,7 +9,6 @@ sealed trait Escape {
|
|||||||
|
|
||||||
object Escape {
|
object Escape {
|
||||||
|
|
||||||
|
|
||||||
final case class Invalid(str: String) extends Escape {
|
final case class Invalid(str: String) extends Escape {
|
||||||
val repr = str
|
val repr = str
|
||||||
}
|
}
|
||||||
@ -21,18 +21,46 @@ object Escape {
|
|||||||
sealed trait Unicode extends Escape
|
sealed trait Unicode extends Escape
|
||||||
object Unicode {
|
object Unicode {
|
||||||
|
|
||||||
final case class Invalid(unicode: Unicode) extends Unicode {
|
/* Note [Circe and Naming] */
|
||||||
|
type Invalid = InvalidUnicode
|
||||||
|
|
||||||
|
/* Note [Circe and Naming] */
|
||||||
|
val Invalid = InvalidUnicode
|
||||||
|
|
||||||
|
/* Note [Circe and Naming] */
|
||||||
|
final case class InvalidUnicode(unicode: Unicode) extends Unicode {
|
||||||
val repr = unicode.repr
|
val repr = unicode.repr
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class U(pfx: String, sfx: String = "") extends Unicode {
|
/* NOTE [Circe and Naming]
|
||||||
val digits: String
|
* Name of the class above cannot be Invalid, as we already have
|
||||||
|
* Escape.Invalid. And to be able to derive JSON serialization with circe
|
||||||
|
* case class names within a trait subtree need to be unique.
|
||||||
|
*
|
||||||
|
* To keep this unpleasant detail hidden from library users, we introduce
|
||||||
|
* aliases for type and object named `Invalid`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
type U16 = _U16
|
||||||
|
final case class _U16(digits: String) extends Unicode {
|
||||||
|
val pfx = "u"
|
||||||
|
val sfx = ""
|
||||||
val repr = pfx + digits + sfx
|
val repr = pfx + digits + sfx
|
||||||
}
|
}
|
||||||
|
|
||||||
final case class U16 private (digits: String) extends U("u")
|
type U32 = _U32
|
||||||
final case class U32 private (digits: String) extends U("U")
|
final case class _U32(digits: String) extends Unicode {
|
||||||
final case class U21 private (digits: String) extends U("u{", "}")
|
val pfx = "U"
|
||||||
|
val sfx = ""
|
||||||
|
val repr = pfx + digits + sfx
|
||||||
|
}
|
||||||
|
|
||||||
|
type U21 = _U21
|
||||||
|
final case class _U21(digits: String) extends Unicode {
|
||||||
|
val pfx = "u{"
|
||||||
|
val sfx = "}"
|
||||||
|
val repr = pfx + digits + sfx
|
||||||
|
}
|
||||||
|
|
||||||
object Validator {
|
object Validator {
|
||||||
val hexChars =
|
val hexChars =
|
||||||
@ -43,8 +71,8 @@ object Escape {
|
|||||||
|
|
||||||
object U16 {
|
object U16 {
|
||||||
def apply(digits: String): Unicode =
|
def apply(digits: String): Unicode =
|
||||||
if (validate(digits)) U16(digits)
|
if (validate(digits)) _U16(digits)
|
||||||
else Invalid(U16(digits))
|
else Invalid(_U16(digits))
|
||||||
def validate(digits: String) = {
|
def validate(digits: String) = {
|
||||||
import Validator._
|
import Validator._
|
||||||
val validLength = digits.length == 4
|
val validLength = digits.length == 4
|
||||||
@ -54,8 +82,8 @@ object Escape {
|
|||||||
}
|
}
|
||||||
object U32 {
|
object U32 {
|
||||||
def apply(digits: String): Unicode =
|
def apply(digits: String): Unicode =
|
||||||
if (validate(digits)) U32(digits)
|
if (validate(digits)) _U32(digits)
|
||||||
else Invalid(U32(digits))
|
else Invalid(_U32(digits))
|
||||||
def validate(digits: String) = {
|
def validate(digits: String) = {
|
||||||
import Validator._
|
import Validator._
|
||||||
val validLength = digits.length == 8
|
val validLength = digits.length == 8
|
||||||
@ -66,8 +94,8 @@ object Escape {
|
|||||||
}
|
}
|
||||||
object U21 {
|
object U21 {
|
||||||
def apply(digits: String): Unicode =
|
def apply(digits: String): Unicode =
|
||||||
if (validate(digits)) U21(digits)
|
if (validate(digits)) _U21(digits)
|
||||||
else Invalid(U21(digits))
|
else Invalid(_U21(digits))
|
||||||
def validate(digits: String) = {
|
def validate(digits: String) = {
|
||||||
import Validator._
|
import Validator._
|
||||||
val validLength = digits.length >= 1 && digits.length <= 6
|
val validLength = digits.length >= 1 && digits.length <= 6
|
||||||
@ -77,66 +105,236 @@ object Escape {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case object Slash extends Escape {
|
||||||
abstract class Simple(val code: Int) extends Escape{
|
val code: Int = '\\'
|
||||||
def name = toString
|
def name: String = toString
|
||||||
val repr = name
|
override val repr = "\\"
|
||||||
|
}
|
||||||
|
case object Quote extends Escape {
|
||||||
|
val code: Int = '\''
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = "\'"
|
||||||
|
}
|
||||||
|
case object RawQuote extends Escape {
|
||||||
|
val code: Int = '"'
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = "\""
|
||||||
}
|
}
|
||||||
|
|
||||||
case object Slash extends Simple('\\') { override val repr = "\\" }
|
|
||||||
case object Quote extends Simple('\'') { override val repr = "\'" }
|
|
||||||
case object RawQuote extends Simple('"') { override val repr = "\"" }
|
|
||||||
|
|
||||||
// Reference: https://en.wikipedia.org/wiki/String_literal
|
// Reference: https://en.wikipedia.org/wiki/String_literal
|
||||||
sealed trait Character extends Simple
|
sealed trait Character extends Escape
|
||||||
object Character {
|
object Character {
|
||||||
case object a extends Simple('\u0007') with Character
|
case object a extends Character {
|
||||||
case object b extends Simple('\u0008') with Character
|
val code: Int = '\u0007'
|
||||||
case object f extends Simple('\u000C') with Character
|
def name: String = toString
|
||||||
case object n extends Simple('\n') with Character
|
override val repr = name
|
||||||
case object r extends Simple('\r') with Character
|
}
|
||||||
case object t extends Simple('\u0009') with Character
|
case object b extends Character {
|
||||||
case object v extends Simple('\u000B') with Character
|
val code: Int = '\u0008'
|
||||||
case object e extends Simple('\u001B') with Character
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object f extends Character {
|
||||||
|
val code: Int = '\u000C'
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object n extends Character {
|
||||||
|
val code: Int = '\n'
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object r extends Character {
|
||||||
|
val code: Int = '\r'
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object t extends Character {
|
||||||
|
val code: Int = '\u0009'
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object v extends Character {
|
||||||
|
val code: Int = '\u000B'
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object e extends Character {
|
||||||
|
val code: Int = '\u001B'
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
val codes = ADT.constructors[Character]
|
val codes = ADT.constructors[Character]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference: https://en.wikipedia.org/wiki/Control_character
|
// Reference: https://en.wikipedia.org/wiki/Control_character
|
||||||
sealed trait Control extends Simple
|
sealed trait Control extends Escape
|
||||||
object Control {
|
object Control {
|
||||||
case object NUL extends Simple(0x00) with Control
|
case object NUL extends Control {
|
||||||
case object SOH extends Simple(0x01) with Control
|
val code: Int = 0x00
|
||||||
case object STX extends Simple(0x02) with Control
|
def name: String = toString
|
||||||
case object ETX extends Simple(0x03) with Control
|
override val repr = name
|
||||||
case object EOT extends Simple(0x04) with Control
|
}
|
||||||
case object ENQ extends Simple(0x05) with Control
|
case object SOH extends Control {
|
||||||
case object ACK extends Simple(0x06) with Control
|
val code: Int = 0x01
|
||||||
case object BEL extends Simple(0x07) with Control
|
def name: String = toString
|
||||||
case object BS extends Simple(0x08) with Control
|
override val repr = name
|
||||||
case object TAB extends Simple(0x09) with Control
|
}
|
||||||
case object LF extends Simple(0x0A) with Control
|
case object STX extends Control {
|
||||||
case object VT extends Simple(0x0B) with Control
|
val code: Int = 0x02
|
||||||
case object FF extends Simple(0x0C) with Control
|
def name: String = toString
|
||||||
case object CR extends Simple(0x0D) with Control
|
override val repr = name
|
||||||
case object SO extends Simple(0x0E) with Control
|
}
|
||||||
case object SI extends Simple(0x0F) with Control
|
case object ETX extends Control {
|
||||||
case object DLE extends Simple(0x10) with Control
|
val code: Int = 0x03
|
||||||
case object DC1 extends Simple(0x11) with Control
|
def name: String = toString
|
||||||
case object DC2 extends Simple(0x12) with Control
|
override val repr = name
|
||||||
case object DC3 extends Simple(0x13) with Control
|
}
|
||||||
case object DC4 extends Simple(0x14) with Control
|
case object EOT extends Control {
|
||||||
case object NAK extends Simple(0x15) with Control
|
val code: Int = 0x04
|
||||||
case object SYN extends Simple(0x16) with Control
|
def name: String = toString
|
||||||
case object ETB extends Simple(0x17) with Control
|
override val repr = name
|
||||||
case object CAN extends Simple(0x18) with Control
|
}
|
||||||
case object EM extends Simple(0x19) with Control
|
case object ENQ extends Control {
|
||||||
case object SUB extends Simple(0x1A) with Control
|
val code: Int = 0x05
|
||||||
case object ESC extends Simple(0x1B) with Control
|
def name: String = toString
|
||||||
case object FS extends Simple(0x1C) with Control
|
override val repr = name
|
||||||
case object GS extends Simple(0x1D) with Control
|
}
|
||||||
case object RS extends Simple(0x1E) with Control
|
case object ACK extends Control {
|
||||||
case object US extends Simple(0x1F) with Control
|
val code: Int = 0x06
|
||||||
case object DEL extends Simple(0x7F) with Control
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object BEL extends Control {
|
||||||
|
val code: Int = 0x07
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object BS extends Control {
|
||||||
|
val code: Int = 0x08
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object TAB extends Control {
|
||||||
|
val code: Int = 0x09
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object LF extends Control {
|
||||||
|
val code: Int = 0x0A
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object VT extends Control {
|
||||||
|
val code: Int = 0x0B
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object FF extends Control {
|
||||||
|
val code: Int = 0x0C
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object CR extends Control {
|
||||||
|
val code: Int = 0x0D
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object SO extends Control {
|
||||||
|
val code: Int = 0x0E
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object SI extends Control {
|
||||||
|
val code: Int = 0x0F
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object DLE extends Control {
|
||||||
|
val code: Int = 0x10
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object DC1 extends Control {
|
||||||
|
val code: Int = 0x11
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object DC2 extends Control {
|
||||||
|
val code: Int = 0x12
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object DC3 extends Control {
|
||||||
|
val code: Int = 0x13
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object DC4 extends Control {
|
||||||
|
val code: Int = 0x14
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object NAK extends Control {
|
||||||
|
val code: Int = 0x15
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object SYN extends Control {
|
||||||
|
val code: Int = 0x16
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object ETB extends Control {
|
||||||
|
val code: Int = 0x17
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object CAN extends Control {
|
||||||
|
val code: Int = 0x18
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object EM extends Control {
|
||||||
|
val code: Int = 0x19
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object SUB extends Control {
|
||||||
|
val code: Int = 0x1A
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object ESC extends Control {
|
||||||
|
val code: Int = 0x1B
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object FS extends Control {
|
||||||
|
val code: Int = 0x1C
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object GS extends Control {
|
||||||
|
val code: Int = 0x1D
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object RS extends Control {
|
||||||
|
val code: Int = 0x1E
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object US extends Control {
|
||||||
|
val code: Int = 0x1F
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
|
case object DEL extends Control {
|
||||||
|
val code: Int = 0x7F
|
||||||
|
def name: String = toString
|
||||||
|
override val repr = name
|
||||||
|
}
|
||||||
val codes = ADT.constructors[Control]
|
val codes = ADT.constructors[Control]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.enso.syntax.text
|
package org.enso.syntax.text
|
||||||
|
|
||||||
import org.enso.data.{Index, Span}
|
import org.enso.data.Index
|
||||||
|
import org.enso.data.Span
|
||||||
import org.enso.flexer
|
import org.enso.flexer
|
||||||
import org.enso.flexer.Reader
|
import org.enso.flexer.Reader
|
||||||
import org.enso.syntax.text.ast.meta.Builtin
|
import org.enso.syntax.text.ast.meta.Builtin
|
||||||
@ -218,7 +219,7 @@ class Parser {
|
|||||||
|
|
||||||
object Parser {
|
object Parser {
|
||||||
type IDMap = Seq[(Span, AST.ID)]
|
type IDMap = Seq[(Span, AST.ID)]
|
||||||
def apply(): Parser = new Parser()
|
def apply(): Parser = new Parser()
|
||||||
private val newEngine = flexer.Parser.compile(ParserDef())
|
private val newEngine = flexer.Parser.compile(ParserDef())
|
||||||
|
|
||||||
//// Exceptions ////
|
//// Exceptions ////
|
||||||
|
@ -303,9 +303,9 @@ class ParserTest extends FlatSpec with Matchers {
|
|||||||
def _amb_group_(i: Int)(t: AST): Macro.Ambiguous =
|
def _amb_group_(i: Int)(t: AST): Macro.Ambiguous =
|
||||||
amb("(", List(List(")")), Shifted(i, t))
|
amb("(", List(List(")")), Shifted(i, t))
|
||||||
|
|
||||||
val amb_group = _amb_group_(0)(_)
|
val amb_group = _amb_group_(0)(_)
|
||||||
val amb_group_ = _amb_group_(1)(_)
|
val amb_group_ = _amb_group_(1)(_)
|
||||||
val amb_group__ = _amb_group_(2)(_)
|
val amb_group__ = _amb_group_(2)(_)
|
||||||
def group_(): Macro.Ambiguous = amb("(", List(List(")")))
|
def group_(): Macro.Ambiguous = amb("(", List(List(")")))
|
||||||
|
|
||||||
def _amb_if(i: Int)(t: AST) =
|
def _amb_if(i: Int)(t: AST) =
|
||||||
|
Loading…
Reference in New Issue
Block a user