Skip to content
This repository has been archived by the owner on Jan 16, 2021. It is now read-only.

Commit

Permalink
2084 - ssyntax replaced with infix
Browse files Browse the repository at this point in the history
Can't say +. anymore. Use (+).

Some corner cases are hard-coded:
  symbols followed by postfix . are called; prn. => (prn)
  prefix : is part of the symbol; :keyword
  postfix ! is part of the symbol, for reverse!, etc.
  prefix ops are high-precedence, but prefix ! is *low*; !cdr.x, etc.

Other ugliness:
  Need user-space hacks to get l.-1, and to assign to ssyntax: (= l.1 34)
  But you can't use similar tricks to get :~ because it starts with a ':'.
  $var is never expanded for infix. But no reason that can't change.
  • Loading branch information
akkartik committed Oct 5, 2012
1 parent e19df5f commit 365a2ce
Show file tree
Hide file tree
Showing 12 changed files with 226 additions and 228 deletions.
63 changes: 51 additions & 12 deletions 014infix.cc
@@ -1,8 +1,14 @@
//// transform infix expressions into prefix
const string extraSymChars = "$?!_";
const string ssyntaxChars = ":~!."; // simple syntax abbreviations; processed later

AstNode transformInfix(AstNode n) {
// special-case: ellipses are for dotted lists, not infix
if (n.isAtom() && n.atom.token == "...")
return n;

if (n.isAtom() && n.atom.token == "!")
return n;

if (n.isAtom() && !containsInfixChar(n.atom.token))
return n;

Expand Down Expand Up @@ -39,6 +45,11 @@ AstNode transformInfix(AstNode n) {
if (infixOpCalledWithoutArgs(n))
return *++n.elems.begin(); // (++) => ++

if (simplePostfixPeriod(n)) {
n.elems.erase(----n.elems.end()); // drop the period
return n;
}

int oldsize = n.elems.size();

// now n is guaranteed to have at least 3 ops
Expand All @@ -47,6 +58,8 @@ AstNode transformInfix(AstNode n) {
auto curr=prev; ++curr;
auto next=curr; ++next;
for (; next != n.elems.end(); ++prev, ++curr, ++next) {
if (curr->atom.token == "...") continue;

if (!isInfixOp(*curr)) {
*curr = transformInfix(*curr);
continue;
Expand Down Expand Up @@ -98,16 +111,26 @@ AstNode transformInfix(AstNode n) {

AstNode tokenizeInfix(AstNode n) {
string var = n.atom.token;

// special-case: :sym is never infix
if (var[0] == ':') return n;
// special-case: $var is never infix
if (var[0] == '$') return n;
// special-case: !x always processes at lowest precedence
if (var[0] == '!') {
var.insert(++var.begin(), ' ');
CodeStream cs(stream(var));
return transformInfix(nextAstNode(cs));
}

string out;
out += var[0];
for (size_t x = 1; x < var.size(); ++x) {
for (size_t x=1; x < var.size(); ++x) {
if ((isInfixChar(var[x]) && isRegularChar(var[x-1])
// special-case: $var is not infix
&& var[x-1]!='$')
// special-case: trailing '!' is never an op
&& (x != var.size()-1 || var[x] != '!'))
||
(isRegularChar(var[x]) && isInfixChar(var[x-1])
// special-case: l.-1 is not infix
&& (x <= 1 || !find(ssyntaxChars, var[x-2]) || var[x-1] != '-' || !isdigit(var[x]))))
(isRegularChar(var[x]) && isInfixChar(var[x-1])))
out += " ";
out += var[x];
}
Expand All @@ -118,30 +141,39 @@ AstNode tokenizeInfix(AstNode n) {


bool isInfixOp(AstNode n) {
if (n == "!") return true;
if (n.isList()) return false;
string s = n.atom.token;
string::iterator p = s.begin();
if (*p != '$' && !isInfixChar(*p))
return false;
for (++p; p != s.end(); ++p)
string::iterator end = s.end();
if (s[s.size()-1] == '!')
--end;
for (++p; p != end; ++p)
if (!isInfixChar(*p))
return false;
return true;
}

bool containsInfixChar(string name) {
if (name == "!") return true;
for (string::iterator p = name.begin(); p != name.end(); ++p) {
if (p == name.begin() && *p == '-')
continue;

if (*p == '!' && p == --name.end())
continue;

if (isInfixChar(*p)) return true;
}
return false;
}

bool isInfixChar(char c) {
if (c == '!') return true; // ! is both infix and not
return !find(punctuationChars, c)
&& !find(quoteAndUnquoteChars, c) && !find(ssyntaxChars, c)
&& !find(quoteAndUnquoteChars, c)
&& !isalnum(c) && !find(extraSymChars, c);
}

Expand All @@ -154,10 +186,17 @@ bool infixOpCalledWithoutArgs(AstNode n) {
list<AstNode>::iterator p = n.elems.begin();
if (*p != Token("(")) return false;
++p;
if (!isInfixOp(*p))
return false;
return isInfixOp(*p);
}

bool simplePostfixPeriod(AstNode n) {
if (!n.isList() || n.elems.size() != 4) return false;
list<AstNode>::iterator p = n.elems.begin();
if (*p != Token("(")) return false;
++p;
if (!p->isAtom()) return false;
++p;
return *p == ")";
return *p == ".";
}

bool parseableAsFloat(string s) {
Expand Down
142 changes: 125 additions & 17 deletions 014infix.test.cc
Expand Up @@ -5,6 +5,13 @@ void test_infix_passes_atoms() {
checkEq(transformInfix(n), n.atom);
}

void test_infix_passes_ellipses() {
CodeStream cs(stream("..."));
AstNode n = nextAstNode(cs);
check(n.isAtom());
checkEq(transformInfix(n), n.atom);
}

void test_infix_passes_atoms2() {
CodeStream cs(stream("-3.2"));
AstNode n = nextAstNode(cs);
Expand Down Expand Up @@ -32,13 +39,29 @@ void test_infix_passes_dollar_vars() {
checkEq(transformInfix(n), n.atom);
}

void test_infix_handles_dollar_op() {
CodeStream cs(stream("$+"));
checkEq(transformInfix(nextAstNode(cs)), Token("$+"));
}

void test_infix_handles_op_without_args() {
CodeStream cs(stream("(+)"));
AstNode n = transformInfix(nextAstNode(cs));
check(n.isAtom());
checkEq(n.atom, Token("+"));
}

void test_infix_handles_sym_with_trailing_period() {
CodeStream cs(stream("op."));
AstNode n = transformInfix(nextAstNode(cs));
check(n.isList());
list<AstNode>::iterator p = n.elems.begin();
checkEq(*p, Token("(")); ++p;
checkEq(*p, Token("op")); ++p;
checkEq(*p, Token(")")); ++p;
check(p == n.elems.end());
}

void test_infix_handles_op_without_args2() {
CodeStream cs(stream("= (+) 3"));
AstNode n = transformInfix(nextAstNode(cs));
Expand Down Expand Up @@ -83,6 +106,45 @@ void test_infix_handles_simple_lists() {
check(p == n.elems.end());
}

void test_infix_passes_wrapped_op() {
CodeStream cs(stream("a (+) b"));
AstNode n = transformInfix(nextAstNode(cs));
check(n.isList());
list<AstNode>::iterator p = n.elems.begin();
checkEq(*p, Token("(")); ++p;
checkEq(*p, Token("a")); ++p;
checkEq(*p, Token("+")); ++p;
checkEq(*p, Token("b")); ++p;
checkEq(*p, Token(")")); ++p;
check(p == n.elems.end());
}

void test_infix_passes_wrapped_op2() {
CodeStream cs(stream("a (:) b"));
AstNode n = transformInfix(nextAstNode(cs));
check(n.isList());
list<AstNode>::iterator p = n.elems.begin();
checkEq(*p, Token("(")); ++p;
checkEq(*p, Token("a")); ++p;
checkEq(*p, Token(":")); ++p;
checkEq(*p, Token("b")); ++p;
checkEq(*p, Token(")")); ++p;
check(p == n.elems.end());
}

void test_infix_passes_ellipses_in_prefix() {
CodeStream cs(stream("a ... b"));
AstNode n = transformInfix(nextAstNode(cs));
check(n.isList());
list<AstNode>::iterator p = n.elems.begin();
checkEq(*p, Token("(")); ++p;
checkEq(*p, Token("a")); ++p;
checkEq(*p, Token("...")); ++p;
checkEq(*p, Token("b")); ++p;
checkEq(*p, Token(")")); ++p;
check(p == n.elems.end());
}

void test_infix_handles_infix_ops_in_prefix() {
CodeStream cs(stream("+ a b"));
AstNode n = transformInfix(nextAstNode(cs));
Expand Down Expand Up @@ -302,50 +364,96 @@ void test_infix_gives_ops_without_spaces_precedence() {
check(p == n.elems.end());
}

void test_infix_handles_ssyntax() {
CodeStream cs(stream("+."));
checkEq(transformInfix(nextAstNode(cs)), Token("+."));
void test_infix_passes_keyword_syms() {
CodeStream cs(stream(":a"));
AstNode n = nextAstNode(cs);
check(n.isAtom());
checkEq(transformInfix(n), n.atom);
}

void test_infix_passes_keyword_syms2() {
CodeStream cs(stream("f :a x"));
AstNode n = transformInfix(nextAstNode(cs));
check(n.isList());
list<AstNode>::iterator p = n.elems.begin();
checkEq(*p, Token("(")); ++p;
checkEq(*p, Token("f")); ++p;
checkEq(*p, Token(":a")); ++p;
checkEq(*p, Token("x")); ++p;
checkEq(*p, Token(")")); ++p;
check(p == n.elems.end());
}

void test_infix_handles_dollar_var() {
CodeStream cs(stream("$+"));
checkEq(transformInfix(nextAstNode(cs)), Token("$+"));
void test_infix_handles_complement_as_usual() {
CodeStream cs(stream("~a.b"));
AstNode n = transformInfix(nextAstNode(cs));
check(n.isList());
list<AstNode>::iterator p = n.elems.begin();
checkEq(*p, Token("(")); ++p;
checkEq(*p, Token(".")); ++p;
AstNode n2 = *p; ++p;
check(n2.isList());
list<AstNode>::iterator p2 = n2.elems.begin();
checkEq(*p2, Token("(")); ++p2;
checkEq(*p2, Token("~")); ++p2;
checkEq(*p2, Token("a")); ++p2;
checkEq(*p2, Token(")")); ++p2;
check(p2 == n2.elems.end());
checkEq(*p, Token("b")); ++p;
checkEq(*p, Token(")")); ++p;
check(p == n.elems.end());
}

void test_infix_handles_negative_numbers_in_ssyntax() {
CodeStream cs(stream("l.-1"));
checkEq(transformInfix(nextAstNode(cs)), Token("l.-1"));
void test_infix_passes_trailing_bang() {
CodeStream cs(stream("a!"));
AstNode n = nextAstNode(cs);
checkEq(transformInfix(n), n.atom);
}

void test_infix_handles_backquote() {
CodeStream cs(stream("`(a + b)"));
void test_infix_handles_infix_bang() {
CodeStream cs(stream("a ! b"));
AstNode n = transformInfix(nextAstNode(cs));
check(n.isList());
list<AstNode>::iterator p = n.elems.begin();
checkEq(*p, Token("`")); ++p;
checkEq(*p, Token("(")); ++p;
checkEq(*p, Token("+")); ++p;
checkEq(*p, Token("!")); ++p;
checkEq(*p, Token("a")); ++p;
checkEq(*p, Token("b")); ++p;
checkEq(*p, Token(")")); ++p;
check(p == n.elems.end());
}

void test_infix_handles_unquote() {
CodeStream cs(stream(",a+b"));
void test_infix_handles_prefix_bang_with_lower_precedence() {
CodeStream cs(stream("!a.b"));
AstNode n = transformInfix(nextAstNode(cs));
check(n.isList());
list<AstNode>::iterator p = n.elems.begin();
checkEq(*p, Token(",")); ++p;
checkEq(*p, Token("(")); ++p;
checkEq(*p, Token("!")); ++p;
AstNode n2 = *p; ++p;
check(n2.isList());
list<AstNode>::iterator p2 = n2.elems.begin();
checkEq(*p2, Token("(")); ++p2;
checkEq(*p2, Token("+")); ++p2;
checkEq(*p2, Token(".")); ++p2;
checkEq(*p2, Token("a")); ++p2;
checkEq(*p2, Token("b")); ++p2;
checkEq(*p2, Token(")")); ++p2;
check(p2 == n2.elems.end());
checkEq(*p, Token(")")); ++p;
check(p == n.elems.end());
}

void test_infix_handles_backquote() {
CodeStream cs(stream("`(a + b)"));
AstNode n = transformInfix(nextAstNode(cs));
check(n.isList());
list<AstNode>::iterator p = n.elems.begin();
checkEq(*p, Token("`")); ++p;
checkEq(*p, Token("(")); ++p;
checkEq(*p, Token("+")); ++p;
checkEq(*p, Token("a")); ++p;
checkEq(*p, Token("b")); ++p;
checkEq(*p, Token(")")); ++p;
check(p == n.elems.end());
}

Expand Down
56 changes: 0 additions & 56 deletions 021ssyntax.cc

This file was deleted.

0 comments on commit 365a2ce

Please sign in to comment.