Skip to content

Commit

Permalink
Feat/build AST for circom parser (#27)
Browse files Browse the repository at this point in the history
* skip error in parse

* setup macro for ast

* impl ast use macro patch 1

* use default trait astnode in rowan

* fmt

* make clippy happy
  • Loading branch information
vuvoth authored Feb 18, 2024
1 parent 1e66014 commit 971b332
Show file tree
Hide file tree
Showing 25 changed files with 517 additions and 405 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ members = ["crates/*", "xtask/"]
[workspace.dependencies]
parser = {path = "./crates/parser", version = "0.1.0"}
vfs = {path = "./crates/vfs", version = "0.1.0"}
syntax = {path = './crates/syntax', version = "0.1.0"}

[workspace.package]
rust-version = "1.71"
20 changes: 18 additions & 2 deletions assets/test.circom
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
pragma circom 2.0.1;

/* This is good teamplatehello
*/
template Adder() {
// config signal for x
signal input x;
x <= 100;
add();
x <== 100;
}


template A() {
component a = Adder();
a.x <== 100;
var b = 10;
var c = b + 10;

signal x;
x <== 10;

signal y;
y <== x + 100;
}


6 changes: 5 additions & 1 deletion crates/lsp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ lsp-server = "0.7.6"
lsp-types = {version = "0.94.1", features = ["proposed"]}
parser.workspace = true
vfs.workspace = true
syntax.workspace = true

anyhow = "1.0.79"
dashmap = "5.5.3"

[profile]
dev.debug = 2
dev.debug = 2
93 changes: 93 additions & 0 deletions crates/lsp/src/global_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use anyhow::Result;
use dashmap::DashMap;
use lsp_server::{RequestId, Response};
use lsp_types::{
DidChangeTextDocumentParams, DidOpenTextDocumentParams, GotoDefinitionParams,
GotoDefinitionResponse, Location, Url,
};
use parser::{
ast::{AstCircomProgram, AstNode},
parser::Parser,
syntax_node::SyntaxNode,
utils::FileUtils,
};

use crate::handler::goto_definition::{lookup_definition, lookup_token_at_postion};

#[derive(Debug)]
pub struct TextDocument {
text: String,
uri: Url,
}

impl From<DidOpenTextDocumentParams> for TextDocument {
fn from(value: DidOpenTextDocumentParams) -> Self {
Self {
text: value.text_document.text,
uri: value.text_document.uri,
}
}
}

impl From<DidChangeTextDocumentParams> for TextDocument {
fn from(value: DidChangeTextDocumentParams) -> Self {
Self {
text: value.content_changes[0].text.to_string(),
uri: value.text_document.uri,
}
}
}

pub struct GlobalState {
pub ast_map: DashMap<String, AstCircomProgram>,
pub file_map: DashMap<String, FileUtils>,
}

impl GlobalState {
pub fn new() -> Self {
Self {
ast_map: DashMap::new(),
file_map: DashMap::new(),
}
}

pub fn goto_definition_handler(&self, id: RequestId, params: GotoDefinitionParams) -> Response {
let uri = params.text_document_position_params.text_document.uri;

let ast = self.ast_map.get(&uri.to_string()).unwrap();
let file = self.file_map.get(&uri.to_string()).unwrap();

let mut result = Some(GotoDefinitionResponse::Array(Vec::new()));

eprintln!("{:?}", params.text_document_position_params.position);
if let Some(token) =
lookup_token_at_postion(&file, &ast, params.text_document_position_params.position)
{
if let Some(range) = lookup_definition(&file, &ast, token) {
result = Some(GotoDefinitionResponse::Scalar(Location::new(uri, range)));
};
}

let result = serde_json::to_value(result).unwrap();

Response {
id,
result: Some(result),
error: None,
}
}

pub(crate) fn handle_update(&mut self, text_document: &TextDocument) -> Result<()> {
let text = &text_document.text;
let url = text_document.uri.to_string();

let green = Parser::parse_circom(text);
let syntax = SyntaxNode::new_root(green);

self.ast_map
.insert(url.clone(), AstCircomProgram::cast(syntax).unwrap());

self.file_map.insert(url, FileUtils::create(text));
Ok(())
}
}
16 changes: 7 additions & 9 deletions crates/lsp/src/handler/goto_definition.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
use crate::SyntaxNode;
use lsp_types::{Position, Range};
use parser::{
ast::{AstNode, CircomProgramAST},
ast::{AstCircomProgram, AstNode},
syntax_node::SyntaxToken,
token_kind::TokenKind,
utils::FileUtils,
};

pub fn lookup_token_at_postion(
file: &FileUtils,
ast: &SyntaxNode,
ast: &AstCircomProgram,
position: Position,
) -> Option<SyntaxToken> {
let off_set = file.off_set(position);
ast.token_at_offset(off_set).find_map(|token| {
ast.syntax().token_at_offset(off_set).find_map(|token| {
let kind = token.kind();

if kind == TokenKind::Identifier {
Expand All @@ -26,21 +25,20 @@ pub fn lookup_token_at_postion(

pub fn lookup_definition(
file: &FileUtils,
ast: &CircomProgramAST,
ast: &AstCircomProgram,
token: SyntaxToken,
) -> Option<Range> {
let template_list = ast.template_list();

for template in template_list {
let template_name = template.template_name().unwrap();
if template_name.name().text() == token.text() {
if template_name.name().unwrap().syntax().text() == token.text() {
let range = Some(Range {
start: file.position(template_name.syntax().text_range().start()),
end: file.position(template_name.syntax().text_range().end()),
start: file.position(template.syntax().text_range().start()),
end: file.position(template.syntax().text_range().end()),
});
return range;
}
}

None
}
1 change: 1 addition & 0 deletions crates/lsp/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod handler;
79 changes: 13 additions & 66 deletions crates/lsp/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,17 @@
#![allow(clippy::print_stderr)]

use std::collections::HashMap;
use global_state::GlobalState;
use std::error::Error;

use lsp_types::notification::{DidChangeTextDocument, DidOpenTextDocument};
use lsp_types::{
request::GotoDefinition, GotoDefinitionResponse, InitializeParams, ServerCapabilities,
};
use lsp_types::{Location, OneOf, TextDocumentSyncCapability, TextDocumentSyncKind};
use lsp_types::{request::GotoDefinition, InitializeParams, ServerCapabilities};
use lsp_types::{OneOf, TextDocumentSyncCapability, TextDocumentSyncKind};

use lsp_server::{Connection, ExtractError, Message, Notification, Request, RequestId, Response};
use parser::ast::{AstNode, CircomProgramAST};
use parser::parser::Parser;
use parser::syntax_node::SyntaxNode;
use parser::utils::{FileId, FileUtils};
use lsp_server::{Connection, ExtractError, Message, Notification, Request, RequestId};

use crate::handler::goto_definition::{lookup_definition, lookup_token_at_postion};
use crate::global_state::TextDocument;

mod global_state;
mod handler;

struct GlobalState {
pub ast: HashMap<String, CircomProgramAST>,
pub file_map: HashMap<String, FileUtils>,
}

fn main() -> Result<(), Box<dyn Error + Sync + Send>> {
// Note that we must have our logging only write out to stderr.
eprintln!("starting generic LSP server");
Expand All @@ -33,7 +21,7 @@ fn main() -> Result<(), Box<dyn Error + Sync + Send>> {
let (connection, io_threads) = Connection::stdio();

// Run the server and wait for the two threads to end (typically by trigger LSP Exit event).
let server_capabilities = serde_json::to_value(&ServerCapabilities {
let server_capabilities = serde_json::to_value(ServerCapabilities {
text_document_sync: Some(TextDocumentSyncCapability::Kind(TextDocumentSyncKind::FULL)),
definition_provider: Some(OneOf::Left(true)),
..Default::default()
Expand Down Expand Up @@ -63,10 +51,7 @@ fn main_loop(
) -> Result<(), Box<dyn Error + Sync + Send>> {
let _params: InitializeParams = serde_json::from_value(params).unwrap();

let mut global_state = GlobalState {
ast: HashMap::new(),
file_map: HashMap::new(),
};
let mut global_state = GlobalState::new();

for msg in &connection.receiver {
match msg {
Expand All @@ -76,69 +61,31 @@ fn main_loop(
}
match cast::<GotoDefinition>(req) {
Ok((id, params)) => {
let uri = params.text_document_position_params.text_document.uri;

let ast = global_state.ast.get(&uri.to_string()).unwrap();
let file = global_state.file_map.get(&uri.to_string()).unwrap();
let token = lookup_token_at_postion(
&file,
ast.syntax(),
params.text_document_position_params.position,
);

let range = lookup_definition(file, &ast, token.unwrap());

let result = Some(GotoDefinitionResponse::Scalar(Location::new(
uri,
range.unwrap(),
)));
let result = serde_json::to_value(&result).unwrap();
let resp = Response {
id,
result: Some(result),
error: None,
};
let resp = global_state.goto_definition_handler(id, params);
connection.sender.send(Message::Response(resp))?;
continue;
}
Err(err @ ExtractError::JsonError { .. }) => panic!("{err:?}"),
Err(ExtractError::MethodMismatch(req)) => req,
};
}

Message::Response(resp) => {
eprintln!("got response: {resp:?}");
}
Message::Notification(not) => {
eprintln!("This is notification: {:?}", not.clone());
match cast_notification::<DidOpenTextDocument>(not.clone()) {
Ok(params) => {
let text = params.text_document.text;
let url = params.text_document.uri.to_string();

let green = Parser::parse_circom(&text);
let syntax = SyntaxNode::new_root(green);

global_state
.ast
.insert(url.clone(), CircomProgramAST::cast(syntax).unwrap());

global_state.file_map.insert(url, FileUtils::create(&text));
global_state.handle_update(&TextDocument::from(params))?;
}
Err(err @ ExtractError::JsonError { .. }) => panic!("{err:?}"),
Err(ExtractError::MethodMismatch(_not)) => (),
};

match cast_notification::<DidChangeTextDocument>(not.clone()) {
Ok(params) => {
let text = &params.content_changes[0].text;
let url = params.text_document.uri.to_string();
let green = Parser::parse_circom(text);
let syntax = SyntaxNode::new_root(green);

global_state
.ast
.insert(url.clone(), CircomProgramAST::cast(syntax).unwrap());

global_state.file_map.insert(url, FileUtils::create(&text));
global_state.handle_update(&TextDocument::from(params))?;
}
Err(err @ ExtractError::JsonError { .. }) => panic!("{err:?}"),
Err(ExtractError::MethodMismatch(_)) => {}
Expand Down
Loading

0 comments on commit 971b332

Please sign in to comment.