use crate::lex::lex;
use crate::SyntaxKind;
use crate::SyntaxKind::*;
use rowan::ast::AstNode;
use std::str::FromStr;
#[derive(Debug)]
pub enum Error {
Io(std::io::Error),
Parse(ParseError),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match &self {
Error::Io(e) => write!(f, "IO error: {}", e),
Error::Parse(e) => write!(f, "Parse error: {}", e),
}
}
}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Error::Io(e)
}
}
impl std::error::Error for Error {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ParseError(Vec<String>);
impl std::fmt::Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
for err in &self.0 {
writeln!(f, "{}", err)?;
}
Ok(())
}
}
impl std::error::Error for ParseError {}
impl From<ParseError> for Error {
fn from(e: ParseError) -> Self {
Error::Parse(e)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Lang {}
impl rowan::Language for Lang {
type Kind = SyntaxKind;
fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
}
fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
kind.into()
}
}
use rowan::GreenNode;
use rowan::GreenNodeBuilder;
#[derive(Debug)]
struct Parse {
green_node: GreenNode,
#[allow(unused)]
errors: Vec<String>,
}
fn parse(text: &str) -> Parse {
struct Parser {
tokens: Vec<(SyntaxKind, String)>,
builder: GreenNodeBuilder<'static>,
errors: Vec<String>,
}
impl Parser {
fn error(&mut self, msg: String) {
self.builder.start_node(ERROR.into());
if self.current().is_some() {
self.bump();
}
self.errors.push(msg);
self.builder.finish_node();
}
fn parse_expr(&mut self) {
self.builder.start_node(EXPR.into());
loop {
match self.current() {
Some(NEWLINE) => {
break;
}
Some(_t) => {
self.bump();
}
None => {
break;
}
}
}
self.builder.finish_node();
}
fn parse_recipe_line(&mut self) {
self.builder.start_node(RECIPE.into());
self.expect(INDENT);
self.expect(TEXT);
self.expect(NEWLINE);
self.builder.finish_node();
}
fn parse_rule(&mut self) {
self.builder.start_node(RULE.into());
self.skip_ws();
self.expect(IDENTIFIER);
self.skip_ws();
if self.tokens.pop().map(|(k, t)| (k, t)) == Some((OPERATOR, ":".to_string())) {
self.builder.token(OPERATOR.into(), ":");
} else {
self.error("expected ':'".into());
}
self.skip_ws();
self.parse_expr();
self.expect(NEWLINE);
loop {
match self.current() {
Some(INDENT) => {
self.parse_recipe_line();
}
Some(NEWLINE) => {
self.bump();
break;
}
_ => {
break;
}
}
}
self.builder.finish_node();
}
fn parse_assignment(&mut self) {
self.builder.start_node(VARIABLE.into());
self.skip_ws();
self.expect(IDENTIFIER);
self.skip_ws();
self.expect(OPERATOR);
self.skip_ws();
self.parse_expr();
self.expect(NEWLINE);
self.builder.finish_node();
}
fn parse(mut self) -> Parse {
self.builder.start_node(ROOT.into());
loop {
match self.find(|&&(k, _)| k == OPERATOR || k == NEWLINE || k == LPAREN) {
Some((OPERATOR, ":")) => {
self.parse_rule();
}
Some((OPERATOR, "?=")) | Some((OPERATOR, "=")) | Some((OPERATOR, ":=")) | Some((OPERATOR, "::=")) | Some((OPERATOR, ":::=")) | Some((OPERATOR, "+=")) | Some((OPERATOR, "!=")) => {
self.parse_assignment();
}
Some((NEWLINE, _)) => {
self.bump();
}
Some(_) | None => {
self.error(format!("unexpected token {:?}", self.current()));
if self.current().is_some() {
self.bump();
}
}
}
if self.current().is_none() {
break;
}
}
self.builder.finish_node();
Parse {
green_node: self.builder.finish(),
errors: self.errors,
}
}
fn bump(&mut self) {
let (kind, text) = self.tokens.pop().unwrap();
self.builder.token(kind.into(), text.as_str());
}
fn current(&self) -> Option<SyntaxKind> {
self.tokens.last().map(|(kind, _)| *kind)
}
fn find(&self, finder: impl FnMut(&&(SyntaxKind, String)) -> bool) -> Option<(SyntaxKind, &str)> {
self.tokens.iter().rev().find(finder).map(|(kind, text)| (*kind, text.as_str()))
}
fn expect(&mut self, expected: SyntaxKind) {
if self.current() != Some(expected) {
self.error(format!("expected {:?}, got {:?}", expected, self.current()));
} else {
self.bump();
}
}
fn skip_ws(&mut self) {
while self.current() == Some(WHITESPACE) {
self.bump()
}
}
}
let mut tokens = lex(text);
tokens.reverse();
Parser {
tokens,
builder: GreenNodeBuilder::new(),
errors: Vec::new(),
}
.parse()
}
type SyntaxNode = rowan::SyntaxNode<Lang>;
#[allow(unused)]
type SyntaxToken = rowan::SyntaxToken<Lang>;
#[allow(unused)]
type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;
impl Parse {
fn syntax(&self) -> SyntaxNode {
SyntaxNode::new_root(self.green_node.clone())
}
fn root(&self) -> Makefile {
Makefile::cast(self.syntax()).unwrap()
}
}
macro_rules! ast_node {
($ast:ident, $kind:ident) => {
#[derive(PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct $ast(SyntaxNode);
impl AstNode for $ast {
type Language = Lang;
fn can_cast(kind: SyntaxKind) -> bool {
kind == $kind
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self(syntax))
} else {
None
}
}
fn syntax(&self) -> &SyntaxNode {
&self.0
}
}
impl ToString for $ast {
fn to_string(&self) -> String {
self.0.text().to_string()
}
}
};
}
ast_node!(Makefile, ROOT);
ast_node!(Rule, RULE);
ast_node!(Identifier, IDENTIFIER);
ast_node!(VariableDefinition, VARIABLE);
impl VariableDefinition {
pub fn name(&self) -> Option<String> {
self.syntax().children_with_tokens().find_map(|it| it.as_token().and_then(|it| {
if it.kind() == IDENTIFIER {
Some(it.text().to_string())
} else {
None
}
}))
}
pub fn raw_value(&self) -> Option<String> {
self.syntax().children().find(|it| it.kind() == EXPR).map(|it| it.text().to_string())
}
}
impl Makefile {
pub fn new() -> Makefile {
let mut builder = GreenNodeBuilder::new();
builder.start_node(ROOT.into());
builder.finish_node();
let syntax = SyntaxNode::new_root(builder.finish());
Makefile(syntax.clone_for_update())
}
pub fn read<R: std::io::Read>(mut r: R) -> Result<Makefile, Error> {
let mut buf = String::new();
r.read_to_string(&mut buf)?;
Ok(buf.parse()?)
}
pub fn read_relaxed<R: std::io::Read>(mut r: R) -> Result<Makefile, Error> {
let mut buf = String::new();
r.read_to_string(&mut buf)?;
let parsed = parse(&buf);
Ok(parsed.root().clone_for_update())
}
pub fn rules(&self) -> impl Iterator<Item = Rule> {
self.syntax()
.children()
.filter_map(Rule::cast)
}
pub fn variable_definitions(&self) -> impl Iterator<Item = VariableDefinition> {
self.syntax()
.children()
.filter_map(VariableDefinition::cast)
}
pub fn add_rule(&mut self, target: &str) -> Rule {
let mut builder = GreenNodeBuilder::new();
builder.start_node(RULE.into());
builder.token(IDENTIFIER.into(), target);
builder.token(OPERATOR.into(), ":");
builder.token(NEWLINE.into(), "\n");
builder.finish_node();
let syntax = SyntaxNode::new_root(builder.finish()).clone_for_update();
let pos = self.0.children().count();
self.0.splice_children(pos..pos, vec![syntax.clone().into()]);
Rule(syntax)
}
}
impl Rule {
pub fn targets(&self) -> impl Iterator<Item = String> {
self.syntax()
.children_with_tokens()
.take_while(|it| it.as_token().map_or(true, |t| t.kind() != OPERATOR))
.filter_map(|it| it.as_token().map(|t| t.text().to_string()))
}
pub fn prerequisites(&self) -> impl Iterator<Item = String> {
self.syntax()
.children()
.find(|it| it.kind() == EXPR)
.into_iter().flat_map(|it| it.children_with_tokens().filter_map(|it| it.as_token().and_then(|t| if t.kind() == IDENTIFIER { Some(t.text().to_string()) } else { None })))
}
}
impl Default for Makefile {
fn default() -> Self {
Self::new()
}
}
impl FromStr for Makefile {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parsed = parse(s);
if parsed.errors.is_empty() {
Ok(parsed.root().clone_for_update())
} else {
Err(ParseError(parsed.errors))
}
}
}
#[test]
fn test_parse_simple() {
const SIMPLE: &str = r#"VARIABLE = value
rule: dependency
command
"#;
let parsed = parse(SIMPLE);
assert_eq!(parsed.errors, Vec::<String>::new());
let node = parsed.syntax();
assert_eq!(
format!("{:#?}", node),
r#"ROOT@0..44
VARIABLE@0..17
IDENTIFIER@0..8 "VARIABLE"
WHITESPACE@8..9 " "
OPERATOR@9..10 "="
WHITESPACE@10..11 " "
EXPR@11..16
IDENTIFIER@11..16 "value"
NEWLINE@16..17 "\n"
NEWLINE@17..18 "\n"
RULE@18..44
IDENTIFIER@18..22 "rule"
OPERATOR@22..23 ":"
WHITESPACE@23..24 " "
EXPR@24..34
IDENTIFIER@24..34 "dependency"
NEWLINE@34..35 "\n"
RECIPE@35..44
INDENT@35..36 "\t"
TEXT@36..43 "command"
NEWLINE@43..44 "\n"
"#
);
let root = parsed.root().clone_for_update();
let mut rules = root.rules().collect::<Vec<_>>();
assert_eq!(rules.len(), 1);
let rule = rules.pop().unwrap();
assert_eq!(rule.targets().collect::<Vec<_>>(), vec!["rule"]);
assert_eq!(rule.prerequisites().collect::<Vec<_>>(), vec!["dependency"]);
let mut variables = root.variable_definitions().collect::<Vec<_>>();
assert_eq!(variables.len(), 1);
let variable = variables.pop().unwrap();
assert_eq!(variable.name(), Some("VARIABLE".to_string()));
assert_eq!(variable.raw_value(), Some("value".to_string()));
}
#[test]
fn test_parse_multiple_prerequisites() {
const MULTIPLE_PREREQUISITES: &str = r#"rule: dependency1 dependency2
command
"#;
let parsed = parse(MULTIPLE_PREREQUISITES);
assert_eq!(parsed.errors, Vec::<String>::new());
let node = parsed.syntax();
assert_eq!(
format!("{:#?}", node),
r#"ROOT@0..40
RULE@0..40
IDENTIFIER@0..4 "rule"
OPERATOR@4..5 ":"
WHITESPACE@5..6 " "
EXPR@6..29
IDENTIFIER@6..17 "dependency1"
WHITESPACE@17..18 " "
IDENTIFIER@18..29 "dependency2"
NEWLINE@29..30 "\n"
RECIPE@30..39
INDENT@30..31 "\t"
TEXT@31..38 "command"
NEWLINE@38..39 "\n"
NEWLINE@39..40 "\n"
"#);
let root = parsed.root().clone_for_update();
let rule = root.rules().next().unwrap();
assert_eq!(rule.targets().collect::<Vec<_>>(), vec!["rule"]);
assert_eq!(
rule.prerequisites().collect::<Vec<_>>(),
vec!["dependency1", "dependency2"]
);
}
#[test]
fn test_add_rule() {
let mut makefile = Makefile::new();
let rule = makefile.add_rule("rule");
assert_eq!(rule.targets().collect::<Vec<_>>(), vec!["rule"]);
assert_eq!(rule.prerequisites().collect::<Vec<_>>(), Vec::<String>::new());
assert_eq!(makefile.to_string(), "rule:\n");
}