Make syntax not depend on parse 📩

This would make it possible to split them into two separate crates.
This commit is contained in:
Laurenz 2020-10-01 11:32:48 +02:00
parent 16f0bd430e
commit 885bfec5d7
11 changed files with 128 additions and 115 deletions

View File

@ -9,7 +9,7 @@ use futures_executor::block_on;
use typstc::export::pdf;
use typstc::font::FontLoader;
use typstc::syntax::LineMap;
use typstc::parse::LineMap;
use typstc::{Feedback, Pass, Typesetter};
fn main() {

View File

@ -310,7 +310,7 @@ macro_rules! impl_ident {
impl TryFromValue for $type {
fn try_from_value(value: Spanned<&Value>, f: &mut Feedback) -> Option<Self> {
if let Value::Ident(ident) = value.v {
let val = $parse(ident.as_str());
let val = $parse(ident);
if val.is_none() {
error!(@f, value.span, "invalid {}", $name);
}
@ -352,6 +352,12 @@ impl_match!(ScaleLength, "number or length",
/// `Into<String>`.
pub struct StringLike(pub String);
impl From<StringLike> for String {
fn from(like: StringLike) -> String {
like.0
}
}
impl Deref for StringLike {
type Target = str;
@ -360,12 +366,6 @@ impl Deref for StringLike {
}
}
impl From<StringLike> for String {
fn from(like: StringLike) -> String {
like.0
}
}
impl_match!(StringLike, "identifier or string",
Value::Ident(Ident(s)) => StringLike(s.clone()),
Value::Str(s) => StringLike(s.clone()),
@ -410,7 +410,7 @@ impl TryFromValue for FontWeight {
}
}
Value::Ident(ident) => {
let weight = Self::from_str(ident.as_str());
let weight = Self::from_str(ident);
if weight.is_none() {
error!(@f, value.span, "invalid font weight");
}

View File

@ -1,9 +1,7 @@
//! Conversion of byte positions to line/column locations.
use std::fmt::{self, Debug, Display, Formatter};
use super::Pos;
use crate::parse::{is_newline_char, Scanner};
use super::Scanner;
use crate::syntax::{Location, Pos};
/// Enables conversion of byte position to locations.
pub struct LineMap<'s> {
@ -18,7 +16,7 @@ impl<'s> LineMap<'s> {
let mut s = Scanner::new(src);
while let Some(c) = s.eat_merging_crlf() {
if is_newline_char(c) {
if is_newline(c) {
line_starts.push(s.index().into());
}
}
@ -47,32 +45,14 @@ impl<'s> LineMap<'s> {
}
}
/// One-indexed line-column position in source code.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct Location {
/// The one-indexed line.
pub line: u32,
/// The one-indexed column.
pub column: u32,
}
impl Location {
/// Create a new location from line and column.
pub fn new(line: u32, column: u32) -> Self {
Self { line, column }
}
}
impl Debug for Location {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
impl Display for Location {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}:{}", self.line, self.column)
/// Whether this character denotes a newline.
pub fn is_newline(character: char) -> bool {
match character {
// Line Feed, Vertical Tab, Form Feed, Carriage Return.
'\n' | '\x0B' | '\x0C' | '\r' |
// Next Line, Line Separator, Paragraph Separator.
'\u{0085}' | '\u{2028}' | '\u{2029}' => true,
_ => false,
}
}

View File

@ -1,9 +1,11 @@
//! Parsing and tokenization.
mod lines;
mod resolve;
mod scanner;
mod tokens;
pub use lines::*;
pub use resolve::*;
pub use scanner::*;
pub use tokens::*;

View File

@ -1,6 +1,6 @@
//! Resolve strings and raw blocks.
use super::{is_newline_char, Scanner};
use super::{is_newline, Scanner};
use crate::syntax::{Ident, Raw};
/// Resolves all escape sequences in a string.
@ -42,8 +42,8 @@ pub fn resolve_string(string: &str) -> String {
out
}
/// Resolve a hexademical escape sequence (only the inner hex letters without
/// braces or `\u`) into a character.
/// Resolve a hexademical escape sequence into a character
/// (only the inner hex letters without braces or `\u`).
pub fn resolve_hex(sequence: &str) -> Option<char> {
u32::from_str_radix(sequence, 16).ok().and_then(std::char::from_u32)
}
@ -71,7 +71,7 @@ pub fn resolve_raw(raw: &str, backticks: usize) -> Raw {
fn split_at_lang_tag(raw: &str) -> (&str, &str) {
let mut s = Scanner::new(raw);
(
s.eat_until(|c| c == '`' || c.is_whitespace() || is_newline_char(c)),
s.eat_until(|c| c == '`' || c.is_whitespace() || is_newline(c)),
s.rest(),
)
}
@ -101,15 +101,15 @@ fn trim_and_split_raw(raw: &str) -> (Vec<String>, bool) {
(lines, had_newline)
}
/// Splits a string into a vector of lines (respecting Unicode & Windows line
/// breaks).
/// Splits a string into a vector of lines
/// (respecting Unicode, Unix, Mac and Windows line breaks).
pub fn split_lines(text: &str) -> Vec<String> {
let mut s = Scanner::new(text);
let mut line = String::new();
let mut lines = Vec::new();
while let Some(c) = s.eat_merging_crlf() {
if is_newline_char(c) {
if is_newline(c) {
lines.push(std::mem::take(&mut line));
} else {
line.push(c);

View File

@ -102,37 +102,14 @@ impl<'s> Scanner<'s> {
pub fn check(&self, f: impl FnMut(char) -> bool) -> bool {
self.peek().map(f).unwrap_or(false)
}
/// Go back to the where the index says.
fn reset(&mut self) {
self.iter = self.src[self.index ..].chars();
}
}
impl<'s> Scanner<'s> {
/// Slice a part out of the source string.
pub fn get<I>(&self, index: I) -> &'s str
where
I: SliceIndex<str, Output = str>,
{
&self.src[index]
}
/// The full source string.
pub fn src(&self) -> &'s str {
self.src
}
/// The full string up to the current index.
pub fn eaten(&self) -> &'s str {
&self.src[.. self.index]
}
/// The string from `start` to the current index.
pub fn eaten_from(&self, start: usize) -> &'s str {
&self.src[start .. self.index]
}
/// The remaining string after the current index.
pub fn rest(&self) -> &'s str {
&self.src[self.index ..]
}
/// The current index in the string.
pub fn index(&self) -> usize {
self.index
@ -147,9 +124,32 @@ impl<'s> Scanner<'s> {
.unwrap_or(0)
}
/// Go back to the where the index says.
fn reset(&mut self) {
self.iter = self.src[self.index ..].chars();
/// Slice a part out of the source string.
pub fn get<I>(&self, index: I) -> &'s str
where
I: SliceIndex<str, Output = str>,
{
&self.src[index]
}
/// The full source string.
pub fn src(&self) -> &'s str {
self.src
}
/// The full source string up to the current index.
pub fn eaten(&self) -> &'s str {
&self.src[.. self.index]
}
/// The source string from `start` to the current index.
pub fn eaten_from(&self, start: usize) -> &'s str {
&self.src[start .. self.index]
}
/// The remaining source string after the current index.
pub fn rest(&self) -> &'s str {
&self.src[self.index ..]
}
}
@ -158,14 +158,3 @@ impl Debug for Scanner<'_> {
write!(f, "Scanner({}|{})", self.eaten(), self.rest())
}
}
/// Whether this character denotes a newline.
pub fn is_newline_char(character: char) -> bool {
match character {
// Line Feed, Vertical Tab, Form Feed, Carriage Return.
'\n' | '\x0B' | '\x0C' | '\r' |
// Next Line, Line Separator, Paragraph Separator.
'\u{0085}' | '\u{2028}' | '\u{2029}' => true,
_ => false,
}
}

View File

@ -1,8 +1,8 @@
//! Tokenization.
use super::{is_newline_char, Scanner};
use super::{is_newline, Scanner};
use crate::length::Length;
use crate::syntax::{Ident, Pos, Span, SpanWith, Spanned, Token};
use crate::syntax::{is_ident, Pos, Span, SpanWith, Spanned, Token};
use TokenMode::*;
@ -115,7 +115,7 @@ impl<'s> Tokens<'s> {
// Uneat the first char if it's a newline, so that it's counted in the
// loop.
if is_newline_char(first) {
if is_newline(first) {
self.s.uneat();
}
@ -127,7 +127,7 @@ impl<'s> Tokens<'s> {
break;
}
if is_newline_char(c) {
if is_newline(c) {
newlines += 1;
}
}
@ -136,7 +136,7 @@ impl<'s> Tokens<'s> {
}
fn read_line_comment(&mut self) -> Token<'s> {
Token::LineComment(self.s.eat_until(is_newline_char))
Token::LineComment(self.s.eat_until(is_newline))
}
fn read_block_comment(&mut self) -> Token<'s> {
@ -277,7 +277,7 @@ fn parse_expr(text: &str) -> Token<'_> {
Token::Number(num / 100.0)
} else if let Ok(length) = text.parse::<Length>() {
Token::Length(length)
} else if Ident::is_ident(text) {
} else if is_ident(text) {
Token::Ident(text)
} else {
Token::Invalid(text)

View File

@ -1,11 +1,9 @@
//! Syntax types.
mod lines;
mod span;
mod token;
mod tree;
pub use lines::*;
pub use span::*;
pub use token::*;
pub use tree::*;

View File

@ -1,6 +1,6 @@
//! Mapping of values to the locations they originate from in source code.
use std::fmt::{self, Debug, Formatter};
use std::fmt::{self, Debug, Display, Formatter};
#[cfg(test)]
use std::cell::Cell;
@ -168,7 +168,7 @@ impl Debug for Span {
}
}
/// A byte position.
/// A byte position in source code.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct Pos(pub u32);
@ -203,6 +203,35 @@ impl Offset for Pos {
impl Debug for Pos {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.0.fmt(f)
Debug::fmt(&self.0, f)
}
}
/// A one-indexed line-column position in source code.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct Location {
/// The one-indexed line.
pub line: u32,
/// The one-indexed column.
pub column: u32,
}
impl Location {
/// Create a new location from line and column.
pub fn new(line: u32, column: u32) -> Self {
Self { line, column }
}
}
impl Debug for Location {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
impl Display for Location {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}:{}", self.line, self.column)
}
}

View File

@ -1,6 +1,7 @@
//! The syntax tree.
use std::fmt::{self, Debug, Formatter};
use std::ops::Deref;
use unicode_xid::UnicodeXID;
@ -234,7 +235,7 @@ pub struct Ident(pub String);
impl Ident {
/// Create a new identifier from a string checking that it is a valid.
pub fn new(ident: impl AsRef<str> + Into<String>) -> Option<Self> {
if Self::is_ident(ident.as_ref()) {
if is_ident(ident.as_ref()) {
Some(Self(ident.into()))
} else {
None
@ -243,21 +244,21 @@ impl Ident {
/// Return a reference to the underlying string.
pub fn as_str(&self) -> &str {
self.0.as_str()
self
}
}
/// Whether the string is a valid identifier.
pub fn is_ident(string: &str) -> bool {
fn is_ok(c: char) -> bool {
c == '-' || c == '_'
}
impl AsRef<str> for Ident {
fn as_ref(&self) -> &str {
self
}
}
let mut chars = string.chars();
if matches!(chars.next(), Some(c) if c.is_xid_start() || is_ok(c)) {
chars.all(|c| c.is_xid_continue() || is_ok(c))
} else {
false
}
impl Deref for Ident {
type Target = str;
fn deref(&self) -> &Self::Target {
self.0.as_str()
}
}
@ -267,6 +268,20 @@ impl Debug for Ident {
}
}
/// Whether the string is a valid identifier.
pub fn is_ident(string: &str) -> bool {
fn is_ok(c: char) -> bool {
c == '-' || c == '_'
}
let mut chars = string.chars();
if matches!(chars.next(), Some(c) if c.is_xid_start() || is_ok(c)) {
chars.all(|c| c.is_xid_continue() || is_ok(c))
} else {
false
}
}
/// A table of expressions.
///
/// # Example
@ -307,7 +322,7 @@ pub struct CallExpr {
impl CallExpr {
/// Evaluate the call expression to a value.
pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value {
let name = self.name.v.as_str();
let name = &self.name.v;
let span = self.name.span;
let args = self.args.eval(ctx, f).await;

View File

@ -18,8 +18,8 @@ use typstc::layout::elements::{LayoutElement, Shaped};
use typstc::layout::MultiLayout;
use typstc::length::Length;
use typstc::paper::PaperClass;
use typstc::parse::LineMap;
use typstc::style::PageStyle;
use typstc::syntax::LineMap;
use typstc::{Feedback, Pass, Typesetter};
const TEST_DIR: &str = "tests";